Http Interface

https://docs.spring.io/spring-framework/reference/web/webflux-http-interface-client.html

在 Spring6.0 中,新增了 HTTP Interface,它允许开发者将 HTTP 服务定义为具有 HTTP 方法的 Java 接口。然后,你可以生成一个实现该接口并执行这些交换的代理。这有助于简化 HTTP 远程访问,并提供选择 API 风格(如同步或响应式)的额外灵活性。

快速开始

首先,使用 @HttpExchange 方法创建接口:

public interface RepositoryService {
    @GetExchange("/todos/{id}")
    ToDo getTodo(@PathVariable("id") Integer id);
}

现在,你可以创建一个在调用时执行请求的代理:

RestClient

RestClient restClient = RestClient.builder().baseUrl("https://jsonplaceholder.typicode.com").build();
RestClientAdapter adapter = RestClientAdapter.create(restClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
Todo todo = service.getTodo(1);

WebClient

如果你选择使用 WebClient 作为 HTTP 客户端,那么你的 Http Interface 中方法的返回类型应该是 Publisher 类型(例如 Mono 或 Flux):

WebClient webClient = WebClient.builder().baseUrl("https://jsonplaceholder.typicode.com").build();
WebClientAdapter adapter = WebClientAdapter.create(webClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
service.getTodo(1).subscribe(System.out::println);

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);
Todo todo = service.getTodo(1);

@HttpExchange 也可以用于类级别,类级别的配置将应用于所有方法:

@HttpExchange(url = "/todos/{id}")
public interface RepositoryService {
    @GetExchange
    ToDo getTodo(@PathVariable("id") Integer id);
}

返回值

HTTP Interface 支持的返回值取决于开发者底层选择的客户端。

适配了 HttpExchangeAdapter 的客户端,如 RestClientRestTemplate 支持同步返回值:

方法返回值
描述

void

执行给定的请求。

HttpHeaders

执行给定的请求并返回响应标头。

ResponseEntity

执行给定的请求并返回 ResponseEntity 状态和标题。

ResponseEntity

执行给定的请求,将响应内容解码为声明的返回类型,并返回 ResponseEntity 状态、标题和解码后的正文。

T

执行给定的请求并将响应内容解码为声明的返回类型。

适配了 ReactorHttpExchangeAdapter 的客户端,如 WebClient 支持上述所有功能以及响应式。下表显示了 Reactor 类型,但你也可以使用通过 ReactiveAdapterRegistry 支持的其他响应式类型:

方法返回值
描述

Mono<Void>

执行给定的请求,并发布响应内容(如果有)。

Mono<HttpHeaders>

执行给定的请求,释放响应内容(如果有),并返回响应标头。

Mono<T>

执行给定的请求并将响应内容解码为声明的返回类型。

Flux<T>

执行给定的请求并将响应内容解码为声明的元素类型的流。

Mono<ResponseEntity<Void>>

执行给定的请求,并释放响应内容(如果有),并返回 ResponseEntity状态和标头。

Mono<ResponseEntity<T>>

执行给定的请求,将响应内容解码为声明的返回类型,并返回ResponseEntity状态、标题和解码后的正文。

Mono<ResponseEntity<Flux<T>>

执行给定的请求,将响应内容解码为声明元素类型的流,并返回ResponseEntity包含状态、标题和解码的响应主体流的内容。

默认情况下,使用 ReactorHttpExchangeAdapter 的同步返回值的超时时间取决于底层 HTTP 客户端的配置。你也可以在适配器级别设置 blockTimeout 值,建议依赖底层 HTTP 客户端的超时设置,因为它在更低的层级操作并提供更多的控制。

错误处理

要定制错误响应处理,开发者需要配置低层 HTTP 客户端的错误处理。

RestClient

默认情况下,RestClient 会将 4xx5xx 响应视为错误,并抛出 RestClientException 异常。你可以使用 RestClient.Builder.defaultStatusHandler 方法自定义错误处理:

RestClient restClient = RestClient.builder()
		.defaultStatusHandler(HttpStatusCode::isError, (request, response) -> ...)
		.build();

RestClientAdapter adapter = RestClientAdapter.create(restClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();

RestTemplate

默认情况下,RestTemplate 会将 4xx5xx 响应视为错误,抛出 RestClientException 异常。你可以使用 ResponseErrorHandler 自定义错误处理:

RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(myErrorHandler);

RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();

WebClient

默认情况下,WebClient 会将 4xx5xx 响应视为错误,并抛出 WebClientResponseException 异常。你可以使用 WebClient.Builder.defaultStatusHandler 方法自定义错误处理:

WebClient webClient = WebClient.builder()
		.defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
		.build();

WebClientAdapter adapter = WebClientAdapter.create(webClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(adapter).build();

最后更新于

这有帮助吗?