REST Clients

  • RestClient:带流畅 API 的同步客户端。

  • WebClient:带流畅 API 的非阻塞、响应式客户端。

  • RestTemplate:带模板方法 API 的同步客户端。

  • HTTP 接口:带生成、动态代理实现的带注解的接口。

这些方法的核心差异是它们执行请求的方式,包括同步还是异步、阻塞还是非阻塞,以及如何处理响应。

Spring Framework 提供了针对 REST 端点进行调用的以下选择:

  • RestClient- 带有 fluent API 的同步客户端。

  • WebClient - 具有 fluent API 的非阻塞响应客户端。

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

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

RestClient

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

Creating a RestClient

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

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

以下示例演示了如何创建默认 RestClient,以及如何构建自定义 RestClient

  • Java

  • Kotlin

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();
val defaultClient = RestClient.create()

val customClient = RestClient.builder()
  .requestFactory(HttpComponentsClientHttpRequestFactory())
  .messageConverters { converters -> converters.add(MyCustomMessageConverter()) }
  .baseUrl("https://example.com")
  .defaultUriVariables(mapOf("variable" to "foo"))
  .defaultHeader("My-Header", "Foo")
  .requestInterceptor(myCustomInterceptor)
  .requestInitializer(myCustomInitializer)
  .build()

Using the RestClient

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

Request URL

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

  • Java

  • Kotlin

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

函数也可用于更多控件,例如指定 request parameters.

默认情况下对字符串 URL 进行编码,但可以通过使用自定义 `uriBuilderFactory`来构建客户端来更改此行为。还可以通过函数或作为 `java.net.URI`来提供 URL,两者均不编码。有关处理和编码 URI 的更多详细信息,请参见 URI Links.

Request headers and body

如果需要,可以通过使用 header(String,String), headers(Consumer<HttpHeaders), 或使用便捷方法 accept(MediaType…​), acceptCharset(Charset…​) 以及其他方法来添加请求头来操作 HTTP 请求。对于可以包含正文 (POSTPUTPATCH) 的 HTTP 请求,其他方法可用于:contentType(MediaType)contentLength(long)

可以设置请求本身正文通过 body(Object),它在内部使用 HTTP Message Conversion。或者,可以使用 ParameterizedTypeReference 来设置请求正文,允许你使用泛型。最后,正文可以设置成写入 OutputStream 的一个回调函数。

Retrieving the response

一旦设置好请求,可以通过调用 retrieve() 来访问 HTTP 响应。响应正文可以通过使用 body(Class) 或对于参数化类型(如列表) body(ParameterizedTypeReference) 来访问。 body 方法将响应内容转换为各种类型——例如,字节可以转换为 String,JSON 可以使用 Jackson 转换为对象,等等(参见 HTTP Message Conversion)。

响应还可以转换为 ResponseEntity,从而可以访问响应头以及响应正文。

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

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

System.out.println(result); 5
1 设置 GET 请求
2 指定要连接到的 URL
3 Retrieve the response
4 将响应转换成字符串
5 Print the result
Kotlin
val result= restClient.get() 1
  .uri("https://example.com") 2
  .retrieve() 3
  .body<String>() 4

println(result) 5
6 设置 GET 请求
7 指定要连接到的 URL
8 Retrieve the response
9 将响应转换成字符串
10 Print the result

可以访问响应状态代码和头通过 ResponseEntity

Java
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
1 为指定 URL 设置 GET 请求
2 将响应转换为 ResponseEntity
3 Print the result
Kotlin
val result = restClient.get() 1
  .uri("https://example.com") 1
  .retrieve()
  .toEntity<String>() 2

println("Response status: " + result.statusCode) 3
println("Response headers: " + result.headers) 3
println("Contents: " + result.body) 3
4 为指定 URL 设置 GET 请求
5 将响应转换为 ResponseEntity
6 Print the result

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

Java
int id = ...;
Pet pet = restClient.get()
  .uri("https://petclinic.example.com/pets/{id}", id) 1
  .accept(APPLICATION_JSON) 2
  .retrieve()
  .body(Pet.class); 3
1 Using URI variables
2 Accept 标头设置为 application/json
3 将 JSON 响应转换为一个 Pet 域对象
Kotlin
val id = ...
val pet = restClient.get()
  .uri("https://petclinic.example.com/pets/{id}", id) 1
  .accept(APPLICATION_JSON) 2
  .retrieve()
  .body<Pet>() 3
4 Using URI variables
5 Accept 标头设置为 application/json
6 将 JSON 响应转换为一个 Pet 域对象

在下一个示例中,RestClient 用于执行一个包含 JSON 的 POST 请求,它同样使用 Jackson 进行了转换。

Java
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
1 创建 Pet 域对象
2 设置一个 POST 请求,以及用于连接的 URL
3 Content-Type 标头设置为 application/json
4 使用 pet 作为 RequestBody
5 将响应转换为一个不包含正文的响应实体。
Kotlin
val pet: Pet = ... 1
val response = restClient.post() 2
  .uri("https://petclinic.example.com/pets/new") 2
  .contentType(APPLICATION_JSON) 3
  .body(pet) 4
  .retrieve()
  .toBodilessEntity() 5
6 创建 Pet 域对象
7 设置一个 POST 请求,以及用于连接的 URL
8 Content-Type 标头设置为 application/json
9 使用 pet 作为 RequestBody
10 将响应转换为一个不包含正文的响应实体。

Error handling

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

Java
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);
1 创建用于返回 404 状态代码的 URL 的 GET 请求
2 设置一个用于所有 4xx 状态代码的状态处理程序
3 Throw a custom exception
Kotlin
val result = restClient.get() 1
  .uri("https://example.com/this-url-does-not-exist") 1
  .retrieve()
  .onStatus(HttpStatusCode::is4xxClientError) { _, response -> 2
    throw MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()) } 3
  .body<String>()
4 创建用于返回 404 状态代码的 URL 的 GET 请求
5 设置一个用于所有 4xx 状态代码的状态处理程序
6 Throw a custom exception

Exchange

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

Java
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;
    }
  });
1 exchange 提供请求和响应
2 在响应具有 4xx 状态代码时抛出异常
3 将响应转换为 Pet 域对象
Kotlin
val result = restClient.get()
  .uri("https://petclinic.example.com/pets/{id}", id)
  .accept(MediaType.APPLICATION_JSON)
  .exchange { request, response -> 1
    if (response.getStatusCode().is4xxClientError()) { 2
      throw MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()) 2
    } else {
      val pet: Pet = convertResponse(response) 3
      pet
    }
  }
4 exchange 提供请求和响应
5 在响应具有 4xx 状态代码时抛出异常
6 将响应转换为 Pet 域对象

HTTP Message Conversion

Jackson JSON Views

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

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 准备就绪,就可以使用 RestClient.post().body(parts)(或 RestTemplate.postForObject)将其用作 POST 请求的正文。

如果 MultiValueMap 含有至少一个非 String 值,那么 Content-Type 会被 FormHttpMessageConverter 设置为 multipart/form-data。如果 MultiValueMapString 值,则 Content-Type 会被默认设置为 application/x-www-form-urlencoded。如果需要,Content-Type 也可被显式设置。

Client Request Factories

为了执行 HTTP 请求,RestClient 使用一个客户端 HTTP 库。这些库是通过 ClientRequestFactory 接口适配的。有许多实现可用:

  • JdkClientHttpRequestFactory for Java’s HttpClient

  • HttpComponentsClientHttpRequestFactory 用 Apache HTTP 组件 HttpClient

  • JettyClientHttpRequestFactory for Jetty’s HttpClient

  • ReactorNettyClientRequestFactory 用 Reactor Netty 的 HttpClient

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

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

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

WebClient

WebClient 是用于执行 HTTP 请求的非阻塞反应式客户端。它在 5.0 中引入,并且为 RestTemplate 提供了一种选择,支持同步、异步和流式处理场景。

WebClient 支持以下内容:

  • Non-blocking I/O

  • Reactive Streams back pressure

  • 在较少的硬件资源下实现高并发

  • 面向函数的 Fluent API,充分利用 Java 8 lambda

  • Synchronous and asynchronous interactions

  • 从服务器流式传输上行或流式传输下行

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

RestTemplate

RestTemplate 提供了 HTTP 客户端库的高级别 API,其形式为传统的 Spring Template 类,它提供了以下重载方法组:

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

Table 1. RestTemplate methods
Method group Description

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

通过对请求准备和响应提取通过回调接口实现完全控制,执行请求的最通用方式。

Initialization

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

RestTemplate 可用于仪表,以生成度量和跟踪。请参见 RestTemplate Observability support 部分。

Body

传递到 RestTemplate 方法和从其返回的对象将借助 HttpMessageConverter 转换为 HTTP 消息并从中进行转换,请参见 HTTP Message Conversion

Migrating from RestTemplate to RestClient

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

Table 2. RestClient equivalents for RestTemplate methods
RestTemplate method RestClient equivalent

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&#8230;&#8203;)

method(HttpMethod) .uri(String, Object&#8230;&#8203;) .headers(Consumer&lt;HttpHeaders&gt;) .body(Object) .retrieve() .toEntity(Class) <<`HttpEntity` 标题和正文必须通过 headers(Consumer&lt;HttpHeaders&gt;)body(Object) 提供给 RestClient。>>

exchange(String, HttpMethod, HttpEntity, Class, Map)

method(HttpMethod) .uri(String, Map) .headers(Consumer&lt;HttpHeaders&gt;) .body(Object) .retrieve() .toEntity(Class) <<`HttpEntity` 请求头和正主体必须通过 headers(Consumer&lt;HttpHeaders&gt;)body(Object) 提供给 RestClient。>>

exchange(URI, HttpMethod, HttpEntity, Class)

method(HttpMethod) .uri(URI) .headers(Consumer&lt;HttpHeaders&gt;) .body(Object) .retrieve() .toEntity(Class) <<`HttpEntity` 请求头和正主体必须通过 headers(Consumer&lt;HttpHeaders&gt;)body(Object) 提供给 RestClient。>>

exchange(String, HttpMethod, HttpEntity, ParameterizedTypeReference, Object&#8230;&#8203;)

method(HttpMethod) .uri(String, Object&#8230;&#8203;) .headers(Consumer&lt;HttpHeaders&gt;) .body(Object) .retrieve() .toEntity(ParameterizedTypeReference) <<`HttpEntity` 请求头和正主体必须通过 headers(Consumer&lt;HttpHeaders&gt;)body(Object) 提供给 RestClient。>>

exchange(String, HttpMethod, HttpEntity, ParameterizedTypeReference, Map)

method(HttpMethod) .uri(String, Map) .headers(Consumer&lt;HttpHeaders&gt;) .body(Object) .retrieve() .toEntity(ParameterizedTypeReference) <<`HttpEntity` 请求头和正主体必须通过 headers(Consumer&lt;HttpHeaders&gt;)body(Object) 提供给 RestClient。>>

exchange(URI, HttpMethod, HttpEntity, ParameterizedTypeReference)

method(HttpMethod) .uri(URI) .headers(Consumer&lt;HttpHeaders&gt;) .body(Object) .retrieve() .toEntity(ParameterizedTypeReference) <<`HttpEntity` 请求头和正主体必须通过 headers(Consumer&lt;HttpHeaders&gt;)body(Object) 提供给 RestClient。>>

exchange(RequestEntity, Class)

method(HttpMethod) .uri(URI) .headers(Consumer&lt;HttpHeaders&gt;) .body(Object) .retrieve() .toEntity(Class) <<`RequestEntity` 方法、URI、标题和正文必须通过 method(HttpMethod)uri(URI)headers(Consumer&lt;HttpHeaders&gt;)body(Object) 提供给 RestClient。>>

exchange(RequestEntity, ParameterizedTypeReference)

method(HttpMethod) .uri(URI) .headers(Consumer&lt;HttpHeaders&gt;) .body(Object) .retrieve() .toEntity(ParameterizedTypeReference) <<`RequestEntity` 方法、URI、请求头和正主体必须通过 method(HttpMethod)uri(URI)headers(Consumer&lt;HttpHeaders&gt;)body(Object) 提供给 RestClient。>>

execute(String, HttpMethod, RequestCallback, ResponseExtractor, Object&#8230;&#8203;)

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 Interface

Spring Framework 允许您将 HTTP 服务定义为带有 @HttpExchange 方法的 Java 接口,您可以将这种接口传递给 HttpServiceProxyFactory,以创建一个通过 HTTP 客户端(如 RestClientWebClient)执行请求的代理,您还可以从 @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);

}

Method Parameters

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

Method argument Description

URI

动态设置请求 URL,覆盖注释的 url 属性。

UriBuilderFactory

提供一个 UriBuilderFactory 以扩展包含 URI 模板和 URI 变量的内容。实际上,它替换了基础客户端的 UriBuilderFactory(以及其基本 URL)。

HttpMethod

使用 Gemini 动态设置请求的 HTTP 方法,覆盖注释的 method 属性

@RequestHeader

添加请求头或多个头。参数可以是具有多个头的 Map&lt;String, ?&gt;MultiValueMap&lt;String, ?&gt;、具有多个值 `Collection&lt;?&gt;`或单个值。支持对非字符串值进行类型转换。

@PathVariable

添加变量在请求 URL 中展开占位符。参数可以是具有多个变量 Map&lt;String, ?&gt;,或单个值。支持对非字符串值进行类型转换。

@RequestAttribute

提供 Object 添加为请求属性。仅受 RestClientWebClient 支持。

@RequestBody

提供请求正文作为要序列化的对象或 Reactive Streams Publisher,例如 MonoFlux 或任何其他通过已配置的 ReactiveAdapterRegistry 支持的异步类型。

@RequestParam

添加请求参数或多个参数。参数可以是具有多个参数 Map&lt;String, ?&gt;MultiValueMap&lt;String, ?&gt;、具有多个值 `Collection&lt;?&gt;`或单个值。支持对非字符串值进行类型转换。当 “content-type” 设置为 “application/x-www-form-urlencoded” 时,请求参数会在请求正文中进行编码。否则,它们会作为 URL 查询参数添加。

@RequestPart

添加请求部分,该部分可以是 String(表单字段)、Resource(文件部分)、Object(要编码的实体,例如 JSON)、HttpEntity(部分内容和头)、Spring Part 或任何上述的 Reactive Streams Publisher

MultipartFile

MultipartFile 添加请求部分,通常在 Spring MVC 控制器中使用,其中它表示上传的文件。

@CookieValue

添加 cookie 或多个 cookie。参数可以是具有多个 cookie Map&lt;String, ?&gt;MultiValueMap&lt;String, ?&gt;、具有多个值 `Collection&lt;?&gt;`或单个值。支持对非字符串值进行类型转换。

Return Values

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

适用于 HttpExchangeAdapter(如 RestClientRestTemplate)的客户端支持同步返回值:

Method return value Description

void

Perform the given request.

HttpHeaders

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

<T>

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

ResponseEntity<Void>

执行给定的请求,并返回具有状态和头的 ResponseEntity

ResponseEntity<T>

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

适用于 ReactorHttpExchangeAdapter(如 WebClient)的客户端支持以上所有内容以及响应式变体,下表显示了 Reactor 类型,但您还可以使用 ReactiveAdapterRegistry 支持的其他响应式类型:

Method return value Description

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 客户端的超时设置,它在更低级别操作并提供更多控制。

Error Handling

为了自定义错误响应处理,您需要配置底层 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.BuilderdefaultStatusHandler 的 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.BuilderdefaultStatusHandler 的 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();

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