前言
Spring 提供了一些 HTTP 客户端类,可以方便地发起 HTTP 请求。如果需要了解更多 Spring Web 的相关内容,可参考Spring Web 指南。
RestTemplate
RestTemplate 是 Spring Web 模块提供的一个同步的 HTTP 客户端,在 Spring 5(SpringBoot 2)中可使用。它提供了一系列的 HTTP 请求方法,非常容易理解,也提供了高层级的 API。
RestTemplate 提供了 HTTP 方法的许多包装方法。如 getXXX, postXXX, putXXX, patchXXX, deleteXXX,其中也提供了对返回类型的转换方法,如 getForObject 直接获取响应对象,getForEntity 返回 ResponseEntity 包装的响应对象,可获取响应状态码、响应头、响应体等数据。
String url = "http://localhost:8080/test";
ResponseBean responseBean = restTemplate.getForObject(url, ResponseBean.class);
// 添加参数
String url = "http://localhost:8080/testParam/{1}/{2}";
ResponseBean responseBean = restTemplate.getForObject(url, ResponseBean.class, "001", "张三");
如果要构建复杂的 URI,可以使用 UriBuilderFactory,参考 Spring URI 操作工具类。
初始化
执行请求时,RestTemplate 通过 ClientRequestFactory 接口初始化 HTTP 实现库,可以使用下面的实现类,可通过构造函数修改。
- JdkClientHttpRequestFactory:Java 的 HttpClient
- HttpComponentsClientHttpRequestFactory:Apache HTTP 组件的 HttpClient
- JettyClientHttpRequestFactory:Jetty 的 HttpClient
- ReactorNettyClientRequestFactory:Reactor Netty 的 HttpClient
- SimpleClientHttpRequestFactory:默认实现
当 Apache 或 Jetty 的 HttpClient 存在时会优先使用,否则,当 java.net.http 包被载入的话使用 Java 的 HttpClient,最后使用默认的 SimpleClientHttpRequestFactory。
请求响应体处理
RestTemplate 使用 HttpMessageConverter 接口来处理请求体和响应体。它会根据请求和响应的媒体类型(MIME)来自动获取处理实现类。下面就是一些常见的实现类,也可添加自定义的实现类。
- StringHttpMessageConverter:处理文本类型,如 MIME 类型为
text/*
,返回text/plain
类型。 - FormHttpMessageConverter:处理表单数据,如
application/x-www-form-urlencoded
类型、multipart/form-data
类型(上传文件)。 - ByteArrayHttpMessageConverter:处理字节数组,可以处理任意类型
*/*
,返回application/octet-stream
类型。 - MarshallingHttpMessageConverter:处理 XML 数据,对应的类型为
text/xml
或application/xml
。 - MappingJackson2HttpMessageConverter:处理 JSON 数据,基于 Jackson 的 ObjectMapper,对应的类型为
application/json
。 - MappingJackson2XmlHttpMessageConverter:处理 XML 数据,基于 Jackson XML 的 XmlMapper,对应的类型为
application/xml
。 - SourceHttpMessageConverter:处理 javax.xml.transform.Source 类型的数据,对应的类型为
text/xml
和application/xml
。
WebClient
WebClient 是 Spring WebFlux 模块提供的一个异步非阻塞的 HTTP 客户端,在 Spring 5(SpringBoot 2)中可使用。它基于 Reactor 实现了响应式的 API。
使用静态方法 create() 或 builder() 来创建 WebClient。
WebClient client = WebClient.builder()
.codecs(configurer -> ... )
.build();
WebClient 一旦创建就不可变了,不过可以通过克隆一个新的来修改。
WebClient client1 = WebClient.builder()
.filter(filterA).filter(filterB).build();
WebClient client2 = client1.mutate()
.filter(filterC).filter(filterD).build();
// client1 has filterA, filterB
// client2 has filterA, filterB, filterC, filterD
codecs 有缓冲数据大小限制,默认是 256 KB,如果遇到以下错误,需要修改配置。
org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on max bytes to buffer
WebClient webClient = WebClient.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024))
.build();
获取响应
使用 retrieve() 方法发起请求获取响应。
WebClient client = WebClient.create("https://example.org");
// 获取 ResponseEntity
Mono<ResponseEntity<Person>> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.toEntity(Person.class);
// 仅获取响应体
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Person.class);
// 获取流
Flux<Quote> result = client.get()
.uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(Quote.class);
异常处理
如果响应返回的状态码是 4XX 或 5XX,则会抛出 WebClientResponseException 异常(或其子类)。要自定义异常处理,使用 onStatus() 方法。
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, response -> ...)
.onStatus(HttpStatusCode::is5xxServerError, response -> ...)
.bodyToMono(Person.class);
Exchange
exchangeToMono() 和 exchangeToFlux() 方法提供了更灵活的请求方式,可自定义异常的处理。
Mono<Person> entityMono = client.get()
.uri("/persons/1")
.accept(MediaType.APPLICATION_JSON)
.exchangeToMono(response -> {
if (response.statusCode().equals(HttpStatus.OK)) {
return response.bodyToMono(Person.class);
}
else {
// 产生错误
return response.createError();
}
});
请求体
请求体可使用异步类型包裹,如 Mono 或 Flux。
Mono<Person> personMono = ... ;
Mono<Void> result = client.post()
.uri("/persons/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.body(personMono, Person.class)
.retrieve()
.bodyToMono(Void.class);
Flux<Person> personFlux = ... ;
Mono<Void> result = client.post()
.uri("/persons/{id}", id)
.contentType(MediaType.APPLICATION_STREAM_JSON)
.body(personFlux, Person.class)
.retrieve()
.bodyToMono(Void.class);
// 使用 bodyValue 快捷方法包裹
Person person = ... ;
Mono<Void> result = client.post()
.uri("/persons/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(person)
.retrieve()
.bodyToMono(Void.class);
要发送表单数据,可使用 MultiValueMap<String, String>
作为请求体对象,会自动将 Content-Type 设置为 application/x-www-form-urlencoded
。
MultiValueMap<String, String> formData = ... ;
Mono<Void> result = client.post()
.uri("/path", id)
.bodyValue(formData)
.retrieve()
.bodyToMono(Void.class);
要发送 Multipart 数据,可使用 MultiValueMap<String, ?>
作为请求体对象,MultipartBodyBuilder 可用于构造请求体。
MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("fieldPart", "fieldValue");
builder.part("filePart1", new FileSystemResource("...logo.png"));
builder.part("jsonPart", new Person("Jason"));
builder.part("myPart", part); // Part from a server request
MultiValueMap<String, HttpEntity<?>> parts = builder.build();
Mono<Void> result = client.post()
.uri("/path", id)
.body(parts)
.retrieve()
.bodyToMono(Void.class);
同步使用
WebClient 也可以用在同步场景中,使用 block() 方法阻塞。
Person person = client.get().uri("/person/{id}", i).retrieve()
.bodyToMono(Person.class)
.block();
List<Person> persons = client.get().uri("/persons").retrieve()
.bodyToFlux(Person.class)
.collectList()
.block();
如果有多个请求同时发送,可在最终结果阻塞,而不是每个请求都阻塞。
Mono<Person> personMono = client.get().uri("/person/{id}", personId)
.retrieve().bodyToMono(Person.class);
Mono<List<Hobby>> hobbiesMono = client.get().uri("/person/{id}/hobbies", personId)
.retrieve().bodyToFlux(Hobby.class).collectList();
Map<String, Object> data = Mono.zip(personMono, hobbiesMono, (person, hobbies) -> {
Map<String, String> map = new LinkedHashMap<>();
map.put("person", person);
map.put("hobbies", hobbies);
return map;
})
.block();
RestClient
RestClient 是 Spring Web 模块提供的一个同步的 HTTP 客户端,在 Spring 6(SpringBoot 3)中可使用。它是 RestTemplate 的升级版本,提供相似的 API。在 Spring 6 中建议使用它替换 RestTemplate。
使用静态方法 create() 或 builder() 来创建 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();
RestClient 提供了 HTTP 方法对应的 get(), post(), put(), patch(), delete() 等方法来发起请求。
int id = 42;
restClient.get()
.uri("https://example.com/orders/{id}", id)
....
RestClient 初始化和 RestTemplate 类似,也会根据情况选择合适的 HttpClient。
获取响应
使用 retrieve() 方法获取响应,使用 body(Class) 或 body(ParameterizedTypeReference) 方法来转换响应体,它也使用 HttpMessageConverter 来处理请求体和响应体。
String result = restClient.get()
.uri("https://example.com")
.retrieve()
.body(String.class);
System.out.println(result);
toEntity() 方法可以获取 ResponseEntity,即可获取响应的状态码、响应头等数据。
ResponseEntity<String> result = restClient.get()
.uri("https://example.com")
.retrieve()
.toEntity(String.class);
System.out.println("Response status: " + result.getStatusCode());
System.out.println("Response headers: " + result.getHeaders());
System.out.println("Contents: " + result.getBody());
异常处理
当响应状态码为 4xx 或 5xx 时,RestClient 会抛出 RestClientException 异常的子类,可使用 onStatus 方法来注册处理函数。
String result = restClient.get()
.uri("https://example.com/this-url-does-not-exist")
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> {
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders())
})
.body(String.class);
Exchange
exchange() 提供了比 retrieve() 方法更多的处理灵活性,不会自动抛出异常,可自行进行处理。
Pet result = restClient.get()
.uri("https://petclinic.example.com/pets/{id}", id)
.accept(APPLICATION_JSON)
.exchange((request, response) -> {
if (response.getStatusCode().is4xxClientError()) {
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders());
}
else {
Pet pet = convertResponse(response);
return pet;
}
});
HTTP Interface
Spring 也提供了通过接口的方式来发起 HTTP 请求,类似于 Feign。它基于 RestClient 或 WebClient,可发起同步或异步响应式请求,在 Spring 6(SpringBoot 3)中可使用。
在接口上使用注解定义。
interface RepositoryService {
@GetExchange("/repos/{owner}/{repo}")
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
// more HTTP exchange methods...
}
然后可以构建一个代理类来发起请求。下面的例子使用 RestClient。
RestClient restClient = RestClient.builder().baseUrl("https://api.github.com/").build();
RestClientAdapter adapter = RestClientAdapter.create(restClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
下面的例子使用 WebClient。
WebClient webClient = WebClient.builder().baseUrl("https://api.github.com/").build();
WebClientAdapter adapter = WebClientAdapter.create(webClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
下面的例子使用 RestTemplate。
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory("https://api.github.com/"));
RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
也可以在类上定义共用参数,这样对所有的方法适用。
@HttpExchange(url = "/repos/{owner}/{repo}", accept = "application/vnd.github.v3+json")
interface RepositoryService {
@GetExchange
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
@PatchExchange(contentType = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
void updateRepository(@PathVariable String owner, @PathVariable String repo,
@RequestParam String name, @RequestParam String description, @RequestParam String homepage);
}
异常处理
我们可以自定义异常处理方法。
对于 RestClient:
RestClient restClient = RestClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, (request, response) -> ...)
.build();
RestClientAdapter adapter = RestClientAdapter.create(restClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
可参考 RestClient.Builder 的 defaultStatusHandler 的实现。
对于 WebClient:
WebClient webClient = WebClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
.build();
WebClientAdapter adapter = WebClientAdapter.create(webClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(adapter).build();
可参考 WebClient.Builder 的 defaultStatusHandler 的实现。
对于 RestTemplate:
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(myErrorHandler);
RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
可参考 RestTemplate 的 setErrorHandler 方法。
以上就是 Spring 提供的常用的 HTTP 客户端。如果觉得有用请点赞、收藏吧!
标签:HTTP,RestClient,Spring,uri,class,retrieve,id,WebClient,客户端 From: https://blog.csdn.net/wwtg9988/article/details/140780646