文章目录
一、概述
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);