跳到主要内容
版本:6.2.3

REST 客户端

ChatGPT-4o-mini 中英对照 REST Clients

Spring 框架提供了以下选项来调用 REST 端点:

  • RestClient - 具有流畅 API 的同步客户端。

  • WebClient - 具有流畅 API 的非阻塞反应式客户端。

  • RestTemplate - 具有模板方法 API 的同步客户端。

  • HTTP Interface - 带有生成的动态代理实现的注解接口。

RestClient​

RestClient 是一个同步的 HTTP 客户端,提供现代化、流畅的 API。它对 HTTP 库进行了抽象,方便将 Java 对象转换为 HTTP 请求,并从 HTTP 响应中创建对象。

创建一个 RestClient​

RestClient 是通过其中一个静态 create 方法创建的。您还可以使用 builder() 来获取一个具有更多选项的构建器,例如指定要使用的 HTTP 库(请参见 Client Request Factories)和要使用的消息转换器(请参见 HTTP Message Conversion),设置默认 URI、默认路径变量、默认请求头,或 uriBuilderFactory,或注册拦截器和初始化器。

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

以下示例展示了如何创建一个默认的 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")
.defaultCookie("My-Cookie", "Bar")
.requestInterceptor(myCustomInterceptor)
.requestInitializer(myCustomInitializer)
.build();

使用 RestClient​

在使用 RestClient 发起 HTTP 请求时,首先需要指定使用哪种 HTTP 方法。这可以通过 method(HttpMethod) 或者便利方法 get()、head()、post() 等来完成。

请求 URL​

接下来,可以使用 uri 方法指定请求的 URI。此步骤是可选的,如果 RestClient 配置了默认 URI,则可以跳过。URL 通常指定为一个 String,并可以包含可选的 URI 模板变量。以下示例配置了一个 GET 请求到 [example.com/orders/42](https://example.com/orders/42):

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

函数还可以用于更多控制,例如指定 请求参数。

字符串 URL 默认是编码的,但可以通过构建一个带有自定义 uriBuilderFactory 的客户端来更改这一点。URL 也可以通过函数或作为 java.net.URI 提供,这两者都不会被编码。有关处理和编码 URI 的更多详细信息,请参见 URI Links。

请求头和主体​

如果需要,可以通过添加请求头来操控 HTTP 请求,使用 header(String, String)、headers(Consumer<HttpHeaders>,或者使用便利方法 accept(MediaType…​)、acceptCharset(Charset…​) 等等。对于可以包含主体的 HTTP 请求(POST、PUT 和 PATCH),还有其他可用的方法:contentType(MediaType) 和 contentLength(long)。

请求体本身可以通过 body(Object) 设置,它内部使用 HTTP 消息转换。另外,请求体也可以使用 ParameterizedTypeReference 设置,这样可以使用泛型。最后,主体可以设置为一个回调函数,该函数写入 OutputStream。

获取响应​

一旦请求设置完成,可以通过在 retrieve() 之后链接方法调用来发送请求。例如,可以使用 retrieve().body(Class) 或 retrieve().body(ParameterizedTypeReference) 来访问响应体,适用于像列表这样的参数化类型。body 方法将响应内容转换为各种类型——例如,字节可以转换为 String,JSON 可以使用 Jackson 转换为对象,等等(请参见 HTTP 消息转换)。

响应也可以转换为 ResponseEntity,这样可以访问响应头以及主体,使用 retrieve().toEntity(Class)。

备注

单独调用 retrieve() 是一个无操作,并返回一个 ResponseSpec。应用程序必须对 ResponseSpec 调用终端操作才能产生任何副作用。如果消费响应对你的用例没有兴趣,你可以使用 retrieve().toBodilessEntity()。

这个示例展示了如何使用 RestClient 执行一个简单的 GET 请求。

String result = restClient.get() 1
.uri("https://example.com") 2
.retrieve() 3
.body(String.class); 4

System.out.println(result); 5
  • 设置一个 GET 请求

  • 指定要连接的 URL

  • 获取响应

  • 将响应转换为字符串

  • 打印结果

通过 ResponseEntity 提供对响应状态码和头部的访问:

ResponseEntity<String> result = restClient.get() 1
.uri("https://example.com") 1
.retrieve()
.toEntity(String.class); 2

System.out.println("Response status: " + result.getStatusCode()); 3
System.out.println("Response headers: " + result.getHeaders()); 3
System.out.println("Contents: " + result.getBody()); 3
  • 为指定的 URL 设置一个 GET 请求

  • 将响应转换为 ResponseEntity

  • 打印结果

RestClient 可以使用 Jackson 库将 JSON 转换为对象。请注意此示例中 URI 变量的使用,以及 Accept 头设置为 JSON。

int id = ...;
Pet pet = restClient.get()
.uri("https://petclinic.example.com/pets/{id}", id) 1
.accept(APPLICATION_JSON) 2
.retrieve()
.body(Pet.class); 3
  • 使用 URI 变量

  • 将 Accept 头设置为 application/json

  • 将 JSON 响应转换为 Pet 域对象

在下一个示例中,RestClient 被用来执行一个包含 JSON 的 POST 请求,该 JSON 再次使用 Jackson 进行转换。

Pet pet = ... 1
ResponseEntity<Void> response = restClient.post() 2
.uri("https://petclinic.example.com/pets/new") 2
.contentType(APPLICATION_JSON) 3
.body(pet) 4
.retrieve()
.toBodilessEntity(); 5
  • 创建一个 Pet 领域对象

  • 设置一个 POST 请求,以及要连接的 URL

  • 将 Content-Type 头设置为 application/json

  • 使用 pet 作为请求体

  • 将响应转换为一个没有主体的响应实体。

错误处理​

默认情况下,RestClient 在获取状态码为 4xx 或 5xx 的响应时,会抛出 RestClientException 的子类。可以使用 onStatus 来覆盖此行为。

String result = restClient.get() 1
.uri("https://example.com/this-url-does-not-exist") 1
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> { 2
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()); 3
})
.body(String.class);
  • 创建一个 GET 请求,针对返回 404 状态码的 URL

  • 为所有 4xx 状态码设置状态处理程序

  • 抛出自定义异常

交易所​

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

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

  • 当响应具有 4xx 状态码时抛出异常

  • 将响应转换为 Pet 域对象

HTTP 消息转换​

Jackson JSON 视图​

要仅序列化对象属性的一个子集,可以指定一个 Jackson JSON View,如下例所示:

MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23"));
value.setSerializationView(User.WithoutPasswordView.class);

ResponseEntity<Void> response = restClient.post() // or RestTemplate.postForEntity
.contentType(APPLICATION_JSON)
.body(value)
.retrieve()
.toBodilessEntity();

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 的情况下,根据文件扩展名确定。如果需要,您可以使用 HttpEntity 包装器显式提供 MediaType。

一旦 MultiValueMap 准备好,你可以将其用作 POST 请求的主体,使用 RestClient.post().body(parts)(或 RestTemplate.postForObject)。

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

客户请求工厂​

要执行 HTTP 请求,RestClient 使用一个客户端 HTTP 库。这些库通过 ClientRequestFactory 接口进行适配。可用的各种实现包括:

  • JdkClientHttpRequestFactory 用于 Java 的 HttpClient

  • HttpComponentsClientHttpRequestFactory 用于 Apache HTTP Components 的 HttpClient

  • JettyClientHttpRequestFactory 用于 Jetty 的 HttpClient

  • ReactorNettyClientRequestFactory 用于 Reactor Netty 的 HttpClient

  • SimpleClientHttpRequestFactory 作为一个简单的默认选项

如果在构建 RestClient 时未指定请求工厂,它将使用 Apache 或 Jetty 的 HttpClient,前提是它们在类路径上可用。否则,如果加载了 java.net.http 模块,它将使用 Java 的 HttpClient。最后,它将退回到简单的默认值。

提示

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

WebClient​

WebClient 是一个非阻塞的、反应式的客户端,用于执行 HTTP 请求。它在 5.0 中引入,提供了 RestTemplate 的替代方案,支持同步、异步和流式场景。

WebClient 支持以下内容:

  • 非阻塞 I/O

  • 响应式流背压

  • 高并发,使用更少的硬件资源

  • 函数式风格,流畅的 API,利用 Java 8 的 lambda 表达式

  • 同步和异步交互

  • 从服务器流式传输或向服务器流式传输

有关更多详细信息,请参见 WebClient。

RestTemplate​

RestTemplate 提供了一个高层次的 API,封装了 HTTP 客户端库,形式为经典的 Spring 模板类。它暴露了以下几组重载方法:

备注

RestClient 提供了一个更现代的 API 用于同步 HTTP 访问。对于异步和流式场景,请考虑反应式的 WebClient。

表 1. RestTemplate 方法

方法组描述
getForObject通过 GET 检索表示。
getForEntity通过 GET 检索 ResponseEntity(即状态、头部和主体)。
headForHeaders通过 HEAD 检索资源的所有头部。
postForLocation通过 POST 创建新资源,并返回响应中的 Location 头部。
postForObject通过 POST 创建新资源,并返回响应中的表示。
postForEntity通过 POST 创建新资源,并返回响应中的表示。
put通过 PUT 创建或更新资源。
patchForObject通过 PATCH 更新资源,并返回响应中的表示。请注意,JDK 的 HttpURLConnection 不支持 PATCH,但 Apache HttpComponents 等支持。
delete通过 DELETE 删除指定 URI 的资源。
optionsForAllow通过 ALLOW 检索资源的允许 HTTP 方法。
exchange前面方法的更通用(且更少偏见)版本,在需要时提供额外的灵活性。它接受一个 RequestEntity(包括 HTTP 方法、URL、头部和主体作为输入)并返回一个 ResponseEntity。

这些方法允许使用 ParameterizedTypeReference 代替 Class 来指定带有泛型的响应类型。
execute执行请求的最通用方式,通过回调接口对请求准备和响应提取进行完全控制。

初始化​

RestTemplate 使用与 RestClient 相同的 HTTP 库抽象。默认情况下,它使用 SimpleClientHttpRequestFactory,但可以通过构造函数进行更改。请参见 Client Request Factories。

备注

RestTemplate 可以进行观察性仪表化,以生成指标和追踪。请参见 RestTemplate 观察性支持 部分。

Body​

传递给 RestTemplate 方法的对象和从中返回的对象通过 HttpMessageConverter 转换为 HTTP 消息,详见 HTTP 消息转换。

从 RestTemplate 迁移到 RestClient​

下表显示了 RestTemplate 方法的 RestClient 等效项。它可以用于从后者迁移到前者。

表 2. RestClient 等价于 RestTemplate 方法

RestTemplate 方法RestClient 等价物
getForObject(String, Class, Object…​)get() .uri(String, Object…​) .retrieve() .body(Class)
getForObject(String, Class, Map)get() .uri(String, Map) .retrieve() .body(Class)
getForObject(URI, Class)get() .uri(URI) .retrieve() .body(Class)
getForEntity(String, Class, Object…​)get() .uri(String, Object…​) .retrieve() .toEntity(Class)
getForEntity(String, Class, Map)get() .uri(String, Map) .retrieve() .toEntity(Class)
getForEntity(URI, Class)get() .uri(URI) .retrieve() .toEntity(Class)
headForHeaders(String, Object…​)head() .uri(String, Object…​) .retrieve() .toBodilessEntity() .getHeaders()
headForHeaders(String, Map)head() .uri(String, Map) .retrieve() .toBodilessEntity() .getHeaders()
headForHeaders(URI)head() .uri(URI) .retrieve() .toBodilessEntity() .getHeaders()
postForLocation(String, Object, Object…​)post() .uri(String, Object…​) .body(Object).retrieve() .toBodilessEntity() .getLocation()
postForLocation(String, Object, Map)post() .uri(String, Map) .body(Object) .retrieve() .toBodilessEntity() .getLocation()
postForLocation(URI, Object)post() .uri(URI) .body(Object) .retrieve() .toBodilessEntity() .getLocation()
postForObject(String, Object, Class, Object…​)post() .uri(String, Object…​) .body(Object) .retrieve() .body(Class)
postForObject(String, Object, Class, Map)post() .uri(String, Map) .body(Object) .retrieve() .body(Class)
postForObject(URI, Object, Class)post() .uri(URI) .body(Object) .retrieve() .body(Class)
postForEntity(String, Object, Class, Object…​)post() .uri(String, Object…​) .body(Object) .retrieve() .toEntity(Class)
postForEntity(String, Object, Class, Map)post() .uri(String, Map) .body(Object) .retrieve() .toEntity(Class)
postForEntity(URI, Object, Class)post() .uri(URI) .body(Object) .retrieve() .toEntity(Class)
put(String, Object, Object…​)put() .uri(String, Object…​) .body(Object) .retrieve() .toBodilessEntity()
put(String, Object, Map)put() .uri(String, Map) .body(Object) .retrieve() .toBodilessEntity()
put(URI, Object)put() .uri(URI) .body(Object) .retrieve() .toBodilessEntity()
patchForObject(String, Object, Class, Object…​)patch() .uri(String, Object…​) .body(Object) .retrieve() .body(Class)
patchForObject(String, Object, Class, Map)patch() .uri(String, Map) .body(Object) .retrieve() .body(Class)
patchForObject(URI, Object, Class)patch() .uri(URI) .body(Object) .retrieve() .body(Class)
delete(String, Object…​)delete() .uri(String, Object…​) .retrieve() .toBodilessEntity()
delete(String, Map)delete() .uri(String, Map) .retrieve() .toBodilessEntity()
delete(URI)delete() .uri(URI) .retrieve() .toBodilessEntity()
optionsForAllow(String, Object…​)options() .uri(String, Object…​) .retrieve() .toBodilessEntity() .getAllow()
optionsForAllow(String, Map)options() .uri(String, Map) .retrieve() .toBodilessEntity() .getAllow()
optionsForAllow(URI)options() .uri(URI) .retrieve() .toBodilessEntity() .getAllow()
exchange(String, HttpMethod, HttpEntity, Class, Object…​)method(HttpMethod) .uri(String, Object…​) .headers(Consumer<HttpHeaders>) .body(Object) .retrieve() .toEntity(Class) [1]
exchange(String, HttpMethod, HttpEntity, Class, Map)method(HttpMethod) .uri(String, Map) .headers(Consumer<HttpHeaders>) .body(Object) .retrieve() .toEntity(Class) [1]
exchange(URI, HttpMethod, HttpEntity, Class)method(HttpMethod) .uri(URI) .headers(Consumer<HttpHeaders>) .body(Object) .retrieve() .toEntity(Class) [1]
exchange(String, HttpMethod, HttpEntity, ParameterizedTypeReference, Object…​)method(HttpMethod) .uri(String, Object…​) .headers(Consumer<HttpHeaders>) .body(Object) .retrieve() .toEntity(ParameterizedTypeReference) [1]
exchange(String, HttpMethod, HttpEntity, ParameterizedTypeReference, Map)method(HttpMethod) .uri(String, Map) .headers(Consumer<HttpHeaders>) .body(Object) .retrieve() .toEntity(ParameterizedTypeReference) [1]
exchange(URI, HttpMethod, HttpEntity, ParameterizedTypeReference)method(HttpMethod) .uri(URI) .headers(Consumer<HttpHeaders>) .body(Object) .retrieve() .toEntity(ParameterizedTypeReference) [1]
exchange(RequestEntity, Class)method(HttpMethod) .uri(URI) .headers(Consumer<HttpHeaders>) .body(Object) .retrieve() .toEntity(Class) [2]
exchange(RequestEntity, ParameterizedTypeReference)method(HttpMethod) .uri(URI) .headers(Consumer<HttpHeaders>) .body(Object) .retrieve() .toEntity(ParameterizedTypeReference) [2]
execute(String, HttpMethod, RequestCallback, ResponseExtractor, Object…​)method(HttpMethod) .uri(String, Object…​) .exchange(ExchangeFunction)
execute(String, HttpMethod, RequestCallback, ResponseExtractor, Map)method(HttpMethod) .uri(String, Map) .exchange(ExchangeFunction)
execute(URI, HttpMethod, RequestCallback, ResponseExtractor)method(HttpMethod) .uri(URI) .exchange(ExchangeFunction)

HTTP 接口​

Spring 框架允许您将 HTTP 服务定义为具有 @HttpExchange 方法的 Java 接口。您可以将这样的接口传递给 HttpServiceProxyFactory 来创建一个代理,该代理通过 HTTP 客户端(例如 RestClient 或 WebClient)执行请求。您还可以从 @Controller 实现该接口以处理服务器请求。

首先创建带有 @HttpExchange 方法的接口:

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 在类型级别上得到支持,适用于所有方法:

@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);

}

方法参数​

注释,HTTP 交换方法支持灵活的方法签名,具有以下方法参数:

方法参数描述
URI动态设置请求的 URL,覆盖注解的 url 属性。
UriBuilderFactory提供一个 UriBuilderFactory 来扩展 URI 模板和 URI 变量。实际上,替换底层客户端的 UriBuilderFactory(及其基础 URL)。
HttpMethod动态设置请求的 HTTP 方法,覆盖注解的 method 属性。
@RequestHeader添加请求头或多个头。参数可以是单个值、一个值的 Collection<?>、Map<String, ?>、MultiValueMap<String, ?>。支持非字符串值的类型转换。头值被添加,不会覆盖已经添加的头值。
@PathVariable添加一个变量以扩展请求 URL 中的占位符。参数可以是一个包含多个变量的 Map<String, ?>,或一个单独的值。支持非字符串值的类型转换。
@RequestAttribute提供一个 Object 作为请求属性。仅由 RestClient 和 WebClient 支持。
@RequestBody提供请求的主体,可以是要序列化的对象,或一个 Reactive Streams Publisher,如 Mono、Flux,或通过配置的 ReactiveAdapterRegistry 支持的任何其他异步类型。
@RequestParam添加请求参数或多个参数。参数可以是一个包含多个参数的 Map<String, ?> 或 MultiValueMap<String, ?>,一个值的 Collection<?>,或一个单独的值。支持非字符串值的类型转换。

当 "content-type" 设置为 "application/x-www-form-urlencoded" 时,请求参数会被编码在请求主体中。否则,它们会作为 URL 查询参数添加。
@RequestPart添加一个请求部分,可以是字符串(表单字段)、Resource(文件部分)、对象(要编码的实体,例如 JSON)、HttpEntity(部分内容和头)、Spring Part,或上述任何内容的 Reactive Streams Publisher。
MultipartFile从 MultipartFile 添加请求部分,通常在 Spring MVC 控制器中使用,表示上传的文件。
@CookieValue添加一个或多个 cookie。参数可以是一个包含多个 cookie 的 Map<String, ?> 或 MultiValueMap<String, ?>,一个值的 Collection<?>,或一个单独的值。支持非字符串值的类型转换。

方法参数不能为 null,除非 required 属性(在参数注释中可用)被设置为 false,或者参数被标记为可选,这由 MethodParameter#isOptional 决定。

自定义参数解析器​

对于更复杂的情况,HTTP 接口不支持将 RequestEntity 类型作为方法参数。这将占用整个 HTTP 请求,并不会改善接口的语义。开发者可以将多个方法参数组合成一个自定义类型,而不是添加许多方法参数,并配置一个专门的 HttpServiceArgumentResolver 实现。

在以下 HTTP 接口中,我们使用自定义的 Search 类型作为参数:

interface RepositoryService {

@GetExchange("/repos/search")
List<Repository> searchRepository(Search search);

}

我们可以实现我们自己的 HttpServiceArgumentResolver,以支持我们自定义的 Search 类型,并将其数据写入即将发送的 HTTP 请求中。

static class SearchQueryArgumentResolver implements HttpServiceArgumentResolver {
@Override
public boolean resolve(Object argument, MethodParameter parameter, HttpRequestValues.Builder requestValues) {
if (parameter.getParameterType().equals(Search.class)) {
Search search = (Search) argument;
requestValues.addRequestParameter("owner", search.owner());
requestValues.addRequestParameter("language", search.language());
requestValues.addRequestParameter("query", search.query());
return true;
}
return false;
}
}

最后,我们可以在设置过程中使用这个参数解析器,并使用我们的 HTTP 接口。

RestClient restClient = RestClient.builder().baseUrl("https://api.github.com/").build();
RestClientAdapter adapter = RestClientAdapter.create(restClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory
.builderFor(adapter)
.customArgumentResolver(new SearchQueryArgumentResolver())
.build();
RepositoryService repositoryService = factory.createClient(RepositoryService.class);

Search search = Search.create()
.owner("spring-projects")
.language("java")
.query("rest")
.build();
List<Repository> repositories = repositoryService.searchRepository(search);

返回值​

支持的返回值取决于底层客户端。

客户端适配了 HttpExchangeAdapter 的如 RestClient 和 RestTemplate 支持同步返回值:

方法返回值描述
void执行给定的请求。
HttpHeaders执行给定的请求并返回响应头。
<T>执行给定的请求并将响应内容解码为声明的返回类型。
ResponseEntity<Void>执行给定的请求并返回一个带有状态和头部的 ResponseEntity。
ResponseEntity<T>执行给定的请求,将响应内容解码为声明的返回类型,并返回一个带有状态、头部和解码后的主体的 ResponseEntity。

客户端适配了 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 对 4xx 和 5xx HTTP 状态码会引发 RestClientException。要自定义此行为,可以注册一个响应状态处理程序,该处理程序适用于通过客户端执行的所有响应:

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

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

有关更多详细信息和选项,例如抑制错误状态代码,请参阅 RestClient.Builder 中 defaultStatusHandler 的 Javadoc。

对于 WebClient:

默认情况下,WebClient 对于 4xx 和 5xx HTTP 状态码会引发 WebClientResponseException。要自定义此行为,可以注册一个响应状态处理程序,该处理程序适用于通过客户端执行的所有响应:

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

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

有关更多详细信息和选项,例如抑制错误状态代码,请参阅 WebClient.Builder 中 defaultStatusHandler 的 Javadoc。

对于 RestTemplate:

默认情况下,RestTemplate 对 4xx 和 5xx HTTP 状态码会抛出 RestClientException。要自定义此行为,可以注册一个错误处理器,该处理器适用于通过客户端执行的所有响应:

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

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

有关更多详细信息和选项,请参阅 RestTemplate 中 setErrorHandler 的 Javadoc 以及 ResponseErrorHandler 层次结构。


1. HttpEntity 的头部和主体必须通过 headers(Consumer<HttpHeaders>) 和 body(Object) 提供给 RestClient。

2。RequestEntity 方法、URI、头部和主体必须通过 method(HttpMethod)、uri(URI)、headers(Consumer<HttpHeaders>) 和 body(Object) 提供给 RestClient。