|
对于最新的稳定版本,请使用 Spring Framework 6.2.10! |
REST 客户端
Spring Framework 提供了以下用于调用 REST 端点的选项:
-
RestClient- 具有流畅 API 的同步客户端。 -
WebClient- 具有流畅 API 的非阻塞、响应式客户端。 -
RestTemplate- 带有模板方法API的同步客户端。 -
HTTP 接口 - 带有生成的动态代理实现的带注释的接口。
RestClient
这RestClient是一个同步 HTTP 客户端,提供现代、流畅的 API。
它提供了对 HTTP 库的抽象,允许方便地从 Java 对象转换为 HTTP 请求,以及从 HTTP 响应创建对象。
创建RestClient
这RestClient是使用静态create方法。
您还可以使用builder()以获取具有更多选项的构建器,例如指定要使用的 HTTP 库(请参阅客户端请求工厂)和要使用的消息转换器(请参阅 HTTP 消息转换),设置默认 URI、默认路径变量、默认请求标头或uriBuilderFactory,或注册拦截器和初始值设定项。
创建(或构建)后,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()
使用RestClient
使用RestClient,首先要指定的是使用哪种 HTTP 方法。
这可以通过method(HttpMethod)或使用方便的方法get(),head(),post(),依此类推。
请求 URL
接下来,可以使用uri方法。
此步骤是可选的,如果RestClient配置了默认 URI。
URL 通常指定为String,带有可选的 URI 模板变量。
以下示例将 GET 请求配置为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)
...
函数还可用于更多控件,例如指定请求参数。
默认情况下,字符串 URL 是编码的,但可以通过使用自定义uriBuilderFactory.
URL 也可以与函数一起提供,也可以作为java.net.URI,两者都未编码。
有关使用 URI 和编码 URI 的更多详细信息,请参阅 URI 链接。
请求标头和正文
如有必要,可以通过添加请求标头来作 HTTP 请求header(String, String),headers(Consumer<HttpHeaders>,或使用方便的方法accept(MediaType…),acceptCharset(Charset…)等等。
对于可以包含正文 (POST,PUT和PATCH),可以使用其他方法:contentType(MediaType)和contentLength(long).
请求正文本身可以通过body(Object),它在内部使用 HTTP 消息转换。
或者,可以使用ParameterizedTypeReference,允许您使用泛型。
最后,可以将正文设置为一个回调函数,该函数写入OutputStream.
检索响应
设置请求后,通过调用retrieve().
可以使用以下命令访问响应正文body(Class)或body(ParameterizedTypeReference)对于列表等参数化类型。
这body方法将响应内容转换为各种类型——例如,字节可以转换为String,可以使用 Jackson 将 JSON 转换为对象,依此类推(参见 HTTP 消息转换)。
响应也可以转换为ResponseEntity,允许访问响应标头和正文。
此示例演示如何RestClient可用于执行简单的GET请求。
-
Java
-
Kotlin
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 | 检索响应 |
| 4 | 将响应转换为字符串 |
| 5 | 打印结果 |
val result= restClient.get() (1)
.uri("https://example.com") (2)
.retrieve() (3)
.body<String>() (4)
println(result) (5)
| 1 | 设置 GET 请求 |
| 2 | 指定要连接的 URL |
| 3 | 检索响应 |
| 4 | 将响应转换为字符串 |
| 5 | 打印结果 |
通过以下方式提供对响应状态代码和标头的访问ResponseEntity:
-
Java
-
Kotlin
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 | 打印结果 |
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)
| 1 | 为指定的 URL 设置 GET 请求 |
| 2 | 将响应转换为ResponseEntity |
| 3 | 打印结果 |
RestClient可以使用 Jackson 库将 JSON 转换为对象。
请注意此示例中 URI 变量的用法,以及Acceptheader 设置为 JSON。
-
Java
-
Kotlin
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 | 使用 URI 变量 |
| 2 | 将Acceptheader 到application/json |
| 3 | 将 JSON 响应转换为Pet域对象 |
val id = ...
val pet = restClient.get()
.uri("https://petclinic.example.com/pets/{id}", id) (1)
.accept(APPLICATION_JSON) (2)
.retrieve()
.body<Pet>() (3)
| 1 | 使用 URI 变量 |
| 2 | 将Acceptheader 到application/json |
| 3 | 将 JSON 响应转换为Pet域对象 |
在下一个示例中,RestClient用于执行包含 JSON 的 POST 请求,该请求再次使用 Jackson 进行转换。
-
Java
-
Kotlin
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-Typeheader 到application/json |
| 4 | 用pet作为请求正文 |
| 5 | 将响应转换为没有正文的响应实体。 |
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)
| 1 | 创建一个Pet域对象 |
| 2 | 设置 POST 请求和要连接到的 URL |
| 3 | 将Content-Typeheader 到application/json |
| 4 | 用pet作为请求正文 |
| 5 | 将响应转换为没有正文的响应实体。 |
错误处理
默认情况下,RestClient抛出一个RestClientException检索带有 4xx 或 5xx 状态代码的响应时。
可以使用onStatus.
-
Java
-
Kotlin
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 | 抛出自定义异常 |
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>()
| 1 | 为返回 404 状态代码的 URL 创建 GET 请求 |
| 2 | 为所有 4xx 状态代码设置状态处理程序 |
| 3 | 抛出自定义异常 |
交换
对于更高级的方案,RestClient通过exchange()方法,可以使用它来代替retrieve().
使用exchange(),因为交换函数已经提供了对完整响应的访问,允许您执行任何必要的错误处理。
-
Java
-
Kotlin
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 域对象 |
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
}
}
| 1 | exchange提供请求和响应 |
| 2 | 当响应具有 4xx 状态代码时抛出异常 |
| 3 | 将响应转换为 Pet 域对象 |
HTTP 消息转换
这spring-web模块包含HttpMessageConverter接口,用于读取和写入 HTTP 请求和响应的正文InputStream和OutputStream.HttpMessageConverter实例在客户端使用(例如,在RestClient)和服务器端(例如,在 Spring MVC REST 控制器中)。
框架中提供了主媒体 (MIME) 类型的具体实现,并且默认情况下,在RestClient和RestTemplate在客户端和RequestMappingHandlerAdapter(请参阅配置消息转换器)。
的几种实现HttpMessageConverter如下所述。请参阅HttpMessageConverterJavadoc以获取完整列表。对于所有转换器,都会使用默认媒体类型,但您可以通过将supportedMediaTypes财产。
| 消息转换器 | 描述 |
|---|---|
|
一 |
|
一 |
|
一 |
|
一 |
|
一 |
|
一 |
|
一 |
默认情况下,RestClient和RestTemplate注册所有内置消息转换器,具体取决于类路径上底层库的可用性。您还可以使用messageConverters()方法RestClient构建器,或通过messageConverters属性RestTemplate.
Jackson JSON 视图
要仅序列化对象属性的子集,可以指定 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();
多部分
要发送多部分数据,您需要提供一个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包含至少一种非Stringvalue,则Content-Type设置为multipart/form-data通过FormHttpMessageConverter. 如果MultiValueMap有String值,则Content-Type默认为application/x-www-form-urlencoded. 如有必要,Content-Type也可以显式设置。
客户端请求工厂
要执行 HTTP 请求,RestClient使用客户端 HTTP 库。这些库通过ClientRequestFactory接口。
有多种实现方式可供选择:
-
JdkClientHttpRequestFactory对于 Java 的HttpClient -
HttpComponentsClientHttpRequestFactory用于 Apache HTTP 组件HttpClient -
JettyClientHttpRequestFactory对于 Jetty'sHttpClient -
ReactorNettyClientRequestFactory用于 Reactor Netty'sHttpClient -
SimpleClientHttpRequestFactory作为简单的默认值
如果未指定请求工厂,则在RestClient已构建,它将使用 Apache 或 JettyHttpClient如果它们在类路径上可用。
否则,如果java.net.http模块,它将使用 Java 的HttpClient.
最后,它将诉诸简单的默认值。
请注意,SimpleClientHttpRequestFactory在访问表示错误的响应状态时可能会引发异常(例如 401)。
如果这是一个问题,请使用任何替代请求工厂。 |
WebClient
WebClient是一个非阻塞、响应式客户端,用于执行 HTTP 请求。这是
在 5.0 中引入,并提供了RestTemplate,支持
同步、异步和流式处理方案。
WebClient支持以下内容:
-
无阻塞 I/O
-
反应流背压
-
高并发,硬件资源少
-
利用 Java 8 lambda 的函数式流畅 API
-
同步和异步交互
-
流式传输到服务器或从服务器流式传输
有关更多详细信息,请参阅 WebClient。
RestTemplate
这RestTemplate以经典 Spring Template 类的形式提供基于 HTTP 客户端库的高级 API。
它公开了以下重载方法组:
这RestClient为同步 HTTP 访问提供了更现代的 API。
对于异步和流式处理方案,请考虑响应式 WebClient。 |
| 方法组 | 描述 |
|---|---|
|
通过 GET 检索表示。 |
|
检索一个 |
|
使用 HEAD 检索资源的所有标头。 |
|
使用 POST 创建新资源并返回 |
|
使用 POST 创建新资源,并从响应中返回表示形式。 |
|
使用 POST 创建新资源,并从响应中返回表示形式。 |
|
使用 PUT 创建或更新资源。 |
|
使用 PATCH 更新资源,并从响应中返回表示。请注意,JDK |
|
使用 DELETE 删除指定 URI 处的资源。 |
|
使用 ALLOW 检索资源允许的 HTTP 方法。 |
|
上述方法的更通用(且不那么固执己见)版本,可在需要时提供额外的灵活性。它接受 这些方法允许使用 |
|
执行请求的最通用方式,完全控制请求通过回调接口进行准备和响应提取。 |
初始化
RestTemplate使用与RestClient.
默认情况下,它使用SimpleClientHttpRequestFactory,但这可以通过构造函数进行更改。
请参阅客户端请求工厂。
RestTemplate可以检测以实现可观测性,以生成指标和跟踪。
请参阅 RestTemplate 可观测性支持部分。 |
身体
传入和返回的对象RestTemplate方法在 HTTP 消息的帮助下与 HTTP 消息进行转换HttpMessageConverter,请参阅 HTTP 消息转换。
从RestTemplate自RestClient
下表显示RestClient等效项RestTemplate方法。
它可用于从后者迁移到前者。
RestTemplate方法 |
RestClient等效 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HTTP 接口
Spring Framework 允许您将 HTTP 服务定义为 Java 接口,其中包含@HttpExchange方法。您可以将这样的接口传递给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 交换方法支持灵活的方法签名,具有以下功能 方法参数:
| 方法参数 | 描述 |
|---|---|
|
动态设置请求的 URL,覆盖注释的 |
|
提供一个 |
|
动态设置请求的 HTTP 方法,覆盖注释的 |
|
添加一个或多个请求标头。参数可以是 |
|
添加一个变量,用于在请求 URL 中展开占位符。参数可以是 |
|
提供 |
|
将请求的正文作为要序列化的对象提供,或者将请求的正文作为
反应流 |
|
添加一个或多个请求参数。参数可以是 什么时候 |
|
添加一个请求部分,可以是 String(表单字段), |
|
从 |
|
添加一个或多个 Cookie。参数可以是 |
返回值
支持的返回值取决于基础客户端。
客户适应HttpExchangeAdapter如RestClient和RestTemplate支持同步返回值:
| 方法返回值 | 描述 |
|---|---|
|
执行给定的请求。 |
|
执行给定的请求并返回响应标头。 |
|
执行给定的请求并将响应内容解码为声明的返回类型。 |
|
执行给定的请求并返回一个 |
|
执行给定的请求,将响应内容解码为声明的返回类型,然后
返回一个 |
客户适应ReactorHttpExchangeAdapter如WebClient,支持以上所有内容
以及反应性变体。下表显示了 Reactor 类型,但您也可以使用
通过ReactiveAdapterRegistry:
| 方法返回值 | 描述 |
|---|---|
|
执行给定的请求,并释放响应内容(如果有)。 |
|
执行给定的请求,释放响应内容(如果有),并返回 响应标头。 |
|
执行给定的请求并将响应内容解码为声明的返回类型。 |
|
执行给定的请求并将响应内容解码为声明的流 元素类型。 |
|
执行给定的请求,并释放响应内容(如果有),并返回 |
|
执行给定的请求,将响应内容解码为声明的返回类型,然后
返回一个 |
|
执行给定的请求,将响应内容解码为声明的流
元素类型,并返回 |
默认情况下,同步返回值的超时ReactorHttpExchangeAdapter取决于底层 HTTP 客户端的配置方式。您可以设置一个blockTimeout值,但我们建议依赖
底层 HTTP 客户端,它在较低级别运行并提供更多控制。
错误处理
要自定义错误响应处理,您需要配置底层 HTTP 客户端。
为RestClient:
默认情况下,RestClient提高RestClientException用于 4xx 和 5xx HTTP 状态代码。要自定义此设置,请注册一个适用于所有响应的响应状态处理程序通过客户端执行:
RestClient restClient = RestClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, (request, response) -> ...)
.build();
RestClientAdapter adapter = RestClientAdapter.create(restClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
有关更多详细信息和选项,例如抑制错误状态代码,请参阅defaultStatusHandler在RestClient.Builder.
为WebClient:
默认情况下,WebClient提高WebClientResponseException用于 4xx 和 5xx HTTP 状态代码。要自定义此设置,请注册一个适用于所有响应的响应状态处理程序通过客户端执行:
WebClient webClient = WebClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
.build();
WebClientAdapter adapter = WebClientAdapter.create(webClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(adapter).build();
有关更多详细信息和选项,例如抑制错误状态代码,请参阅defaultStatusHandler在WebClient.Builder.
为RestTemplate:
默认情况下,RestTemplate提高RestClientException对于 4xx 和 5xx HTTP 状态代码。要自定义此设置,请注册一个适用于所有响应的错误处理程序通过客户端执行:
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(myErrorHandler);
RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
有关更多详细信息和选项,请参阅setErrorHandler在RestTemplate和 这ResponseErrorHandler等级制度。
RequestEntity方法、URI、标头和正文必须提供给RestClient通过method(HttpMethod),uri(URI),headers(Consumer<HttpHeaders>)和body(Object).