Spring6.0新特性-RestClient:使用更友好的Http客户端

一、概述

1、官网

https://docs.spring.io/spring-framework/reference/6.1/integration/rest-clients.html#rest-restclient

spring6.0官方推出了一个全新的HTTP客户端RestClient
它是一个同步HTTP客户端,它提供了一个现代的、流畅的API。提供了对HTTP库的抽象,允许从Java对象到HTTP请求的方便转换,以及从HTTP响应创建对象。

二、使用

1、创建RestClient

这RestClient是使用一个静态的create方法。你也可以使用builder()要获得具有更多选项的构建器,如指定使用哪个HTTP库(请参见客户端请求工厂)以及使用哪些消息转换器(请参见HTTP消息转换),设置默认URI、默认路径变量、默认请求头或uriBuilderFactory或者注册拦截器和初始化器。

一旦创建(或构建)了RestClient可以被多线程安全地使用

RestClient defaultClient = RestClient.create();

RestClient customClient = RestClient.builder()
  .requestFactory(new HttpComponentsClientHttpRequestFactory())
  .messageConverters(converters -> converters.add(new MyCustomMessageConverter()))
  .baseUrl("https://example.com")
  .defaultUriVariables(Map.of("variable", "foo"))
  .defaultHeader("My-Header", "Foo")
  .requestInterceptor(myCustomInterceptor)
  .requestInitializer(myCustomInitializer)
  .build();

2、使用

(1)请求URL

首先要指定的是使用哪种HTTP方法。这可以通过method(HttpMethod)或者用方便的方法get(), head(), post(),等等。

默认情况下,字符串URL是编码的.

int id = 42;
restClient.get()
  .uri("https://example.com/orders/{id}", id)
  ....

(2)headers 和body

如果有必要,可以通过添加带有header(String, String), headers(Consumer<HttpHeaders>,或者用方便的方法accept(MediaType…​), acceptCharset(Charset…​)诸如此类。对于可以包含正文的HTTP请求(POST, PUT,以及PATCH),还可以使用其他方法:contentType(MediaType),以及contentLength(long)

请求主体本身可以通过以下方式设置body(Object),它在内部使用HTTP消息转换。或者,可以使用ParameterizedTypeReference,允许您使用泛型。最后,可以将主体设置为一个回调函数,该函数写入OutputStream

(3)获取响应

String result = restClient.get()
  .uri("https://example.com")
  .retrieve()
  .body(String.class); // 响应转为字符串

System.out.println(result);
ResponseEntity<String> result = restClient.get()
  .uri("https://example.com")
  .retrieve()
  .toEntity(String.class); // 响应转为ResponseEntity

System.out.println("Response status: " + result.getStatusCode());
System.out.println("Response headers: " + result.getHeaders());
System.out.println("Contents: " + result.getBody());
int id = ...;
Pet pet = restClient.get()
  .uri("https://petclinic.example.com/pets/{id}", id)
  .accept(APPLICATION_JSON)
  .retrieve()
  .body(Pet.class); // 将响应json转为实体类
Pet pet = ...
ResponseEntity<Void> response = restClient.post()
  .uri("https://petclinic.example.com/pets/new")
  .contentType(APPLICATION_JSON)
  .body(pet) // post请求将实体类转为json
  .retrieve()
  .toBodilessEntity();

(4)错误处理

默认情况下,RestClient引发的子类RestClientException检索带有4xx或5xx状态代码的响应时。此行为可以使用onStatus

String result = restClient.get()
  .uri("https://example.com/this-url-does-not-exist")
  .retrieve()
  .onStatus(HttpStatusCode::is4xxClientError, (request, response) -> { // 4xx响应码抛自定义异常
      throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders());
  })
  .body(String.class);

(5)Exchange

对于更高级的场景RestClient通过提供对基础HTTP请求和响应的访问exchange()方法,该方法可以用来代替retrieve()。使用时不应用状态处理程序exchange(),因为交换函数已经提供了对完整响应的访问,允许您执行任何必要的错误处理。

Pet result = restClient.get()
  .uri("https://petclinic.example.com/pets/{id}", id)
  .accept(APPLICATION_JSON)
  .exchange((request, response) -> { // exchange提供请求和响应
    if (response.getStatusCode().is4xxClientError()) {
      throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders());
    }
    else {
      Pet pet = convertResponse(response); // 将响应转换成Pet域对象
      return pet;
    }
  });

(6)Multipart数据

要发送Multipart数据,需要提供一个MultiValueMap<String, Object>其值可能是Object对于零件内容,一个Resource对于文件部分,或HttpEntity对于带标题的零件内容。例如:

MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();

parts.add("fieldPart", "fieldValue");
parts.add("filePart", new FileSystemResource("...logo.png"));
parts.add("jsonPart", new Person("Jason"));

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
parts.add("xmlPart", new HttpEntity<>(myBean, headers));

// send using RestClient.post or RestTemplate.postForEntity

在大多数情况下,您不必指定Content-Type对于每一部分。内容类型是根据HttpMessageConverter选择序列化它,或者,在Resource,基于文件扩展名。如有必要,您可以显式提供MediaType用一个HttpEntity包装纸。

一旦MultiValueMap已经准备好了,您可以将它用作POST请求,使用RestClient.post().body(parts)(或者RestTemplate.postForObject).

如果MultiValueMap包含至少一个非String价值Content-Type设置为multipart/form-data由FormHttpMessageConverter。如果MultiValueMap有String价值观Content-Type默认为application/x-www-form-urlencoded。如有必要Content-Type也可以显式设置。

(7)客户端请求工厂

为了执行HTTP请求,RestClient使用客户端HTTP库。这些库通过ClientRequestFactory界面。有多种实现方式可供选择:

JdkClientHttpRequestFactory对于Java的HttpClient(可以选择用这个)
HttpComponentsClientHttpRequestFactory用于Apache HTTP组件HttpClient
JettyClientHttpRequestFactory对于码头的HttpClient
ReactorNettyClientRequestFactory对于反应器Netty的HttpClient
SimpleClientHttpRequestFactory作为一个简单的默认

如果没有指定请求工厂RestClient建成后,它将使用apache或Jetty的HttpClient如果它们在类路径中可用的话。否则,如果java.net.http模块,它将使用Java的HttpClient。最后,它将求助于简单的缺省。

请注意SimpleClientHttpRequestFactory当访问表示错误的响应状态(例如401)时,可能会引发异常。如果这是一个问题,请使用任何替代的请求工厂。

JdkClientHttpRequestFactory factory= new JdkClientHttpRequestFactory();
// 设置连接池的参数
factory.setReadTimeout(Duration.ofSeconds(10)); // 连接的超时时间
RestClient.builder().requestFactory(factory);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃了也弱了。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值