对于最新的稳定版本,请使用 Spring Framework 6.2.0! |
REST 客户端
Spring 框架提供了以下选项来调用 REST 端点:
-
WebClient
- 具有 Fluent API 的非阻塞反应式客户端。 -
RestTemplate
- 与模板方法 API 同步客户端。 -
HTTP 接口 - 带有生成的动态代理实现的带注释的接口。
WebClient
WebClient
是执行 HTTP 请求的非阻塞反应式客户端。它是
在 5.0 中引入,并提供了RestTemplate
,支持
同步、异步和流式处理方案。
WebClient
支持以下内容:
-
非阻塞 I/O。
-
反应流背压。
-
高并发性,硬件资源较少。
-
函数式的 Fluent API,利用 Java 8 lambda。
-
同步和异步交互。
-
向服务器流式传输或从服务器向式传输。
有关更多详细信息,请参阅 WebClient 。
RestTemplate
这RestTemplate
通过 HTTP 客户端库提供更高级别的 API。它使它
易于在单行中调用 REST 端点。它公开了以下几组
重载方法:
RestTemplate 处于维护模式,仅请求次要
要接受的更改和错误。请考虑改用 WebClient。 |
“方法”组 | 描述 |
---|---|
|
通过 GET 检索表示形式。 |
|
检索 |
|
使用 HEAD 检索资源的所有标头。 |
|
使用 POST 创建新资源并返回 |
|
使用 POST 创建新资源,并从响应中返回表示形式。 |
|
使用 POST 创建新资源,并从响应中返回表示形式。 |
|
使用 PUT 创建或更新资源。 |
|
使用 PATCH 更新资源并从响应中返回表示形式。
请注意,JDK |
|
使用 DELETE 删除指定 URI 处的资源。 |
|
使用 ALLOW 检索资源允许的 HTTP 方法。 |
|
上述方法的更通用(且不那么固执己见)版本,它提供额外的
需要时灵活。它接受一个 这些方法允许使用 |
|
执行请求的最通用方式,可完全控制请求 通过回调接口进行准备和响应提取。 |
初始化
默认构造函数使用java.net.HttpURLConnection
执行请求。您可以
切换到其他 HTTP 库,并使用ClientHttpRequestFactory
.
目前,还内置了对 Apache HttpComponents 和 OkHttp 的支持。
例如,要切换到 Apache HttpComponents,您可以使用以下内容:
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
每ClientHttpRequestFactory
公开特定于底层的配置选项
HTTP 客户端库 — 例如,用于凭证、连接池和其他详细信息。
请注意,java.net HTTP 请求的实现可能会引发异常
访问表示错误的响应的状态(如 401)。如果这是一个
问题,请切换到另一个 HTTP 客户端库。 |
RestTemplate 可以进行检测以实现可观测性,以便生成指标和跟踪。
请参阅 RestTemplate 可观察性支持部分。 |
URI
许多RestTemplate
方法接受 URI 模板和 URI 模板变量,
要么作为String
variable 参数或Map<String,String>
.
以下示例使用String
variable 参数:
String result = restTemplate.getForObject(
"https://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");
以下示例使用Map<String, String>
:
Map<String, String> vars = Collections.singletonMap("hotel", "42");
String result = restTemplate.getForObject(
"https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
请记住,URI 模板是自动编码的,如下例所示:
restTemplate.getForObject("https://example.com/hotel list", String.class);
// Results in request to "https://example.com/hotel%20list"
您可以使用uriTemplateHandler
的属性RestTemplate
自定义 URI 的方式
进行编码。或者,您可以准备一个java.net.URI
并将其传递到
这RestTemplate
方法,该方法接受URI
.
有关使用 URI 和编码 URI 的更多详细信息,请参阅 URI 链接。
头
您可以使用exchange()
方法指定请求标头,如下例所示:
String uriTemplate = "https://example.com/hotels/{hotel}";
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).build(42);
RequestEntity<Void> requestEntity = RequestEntity.get(uri)
.header("MyRequestHeader", "MyValue")
.build();
ResponseEntity<String> response = template.exchange(requestEntity, String.class);
String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();
您可以通过许多RestTemplate
method 变体,返回ResponseEntity
.
身体
传入和返回的对象RestTemplate
方法与 Raw 相互转换
content 在HttpMessageConverter
.
在 POST 上,输入对象将序列化为请求正文,如下例所示:
URI location = template.postForLocation("https://example.com/people", person);
您无需显式设置请求的 Content-Type 标头。在大多数情况下,
您可以根据来源找到兼容的消息转换器Object
type 和所选的
Message Converter 会相应地设置内容类型。如有必要,您可以使用exchange
方法显式提供Content-Type
request 标头,并且
turn 影响选择的消息转换器。
在 GET 上,响应的主体被反序列化为输出Object
,如下例所示:
Person person = restTemplate.getForObject("https://example.com/people/{id}", Person.class, 42);
这Accept
header 不需要显式设置。在大多数情况下,
可以根据预期的响应类型找到兼容的消息转换器,该
然后帮助填充Accept
页眉。如有必要,您可以使用exchange
方法提供Accept
标头。
默认情况下,RestTemplate
注册所有内置消息转换器,具体取决于 Classpath 检查,这有助于
来确定存在哪些可选的转换库。您还可以设置消息
转换器来显式使用。
消息转换
这spring-web
module 包含HttpMessageConverter
Contract 进行读取和
通过InputStream
和OutputStream
.HttpMessageConverter
实例在客户端使用(例如,在RestTemplate
) 和
在服务器端(例如,在 Spring MVC REST 控制器中)。
框架中提供了主媒体 (MIME) 类型的具体实现
,默认情况下,它们已注册到RestTemplate
在客户端,使用RequestMappingHandlerAdapter
在服务器端(请参阅 配置消息转换器)。
的HttpMessageConverter
在以下各节中进行了介绍。
对于所有转换器,都使用默认媒体类型,但您可以通过设置supportedMediaTypes
bean 属性。下表描述了每种实现:
消息转换器 | 描述 |
---|---|
|
一 |
|
一 |
|
一 |
|
一 |
|
一 |
|
一 |
|
一 |
|
一 |
Jackson JSON 视图
您可以指定 Jackson JSON 视图以仅序列化对象属性的子集,如下例所示:
MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23"));
value.setSerializationView(User.WithoutPasswordView.class);
RequestEntity<MappingJacksonValue> requestEntity =
RequestEntity.post(new URI("https://example.com/user")).body(value);
ResponseEntity<String> response = template.exchange(requestEntity, String.class);
多部分
要发送多部分数据,您需要提供MultiValueMap<String, Object>
其值
可能是Object
对于部件内容,一个Resource
对于文件部分,或者HttpEntity
为
带有标题的 part 内容。例如:
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));
在大多数情况下,您不必指定Content-Type
对于每个部分。内容
type 是根据HttpMessageConverter
选择要序列化
it 或Resource
基于文件扩展名。如有必要,您可以
显式提供MediaType
替换为HttpEntity
包装纸。
一旦MultiValueMap
已准备就绪,您可以将其传递给RestTemplate
,如下所示:
MultiValueMap<String, Object> parts = ...;
template.postForObject("https://example.com/upload", parts, Void.class);
如果MultiValueMap
包含至少一个非String
值、Content-Type
已设置
自multipart/form-data
由FormHttpMessageConverter
.如果MultiValueMap
具有String
值Content-Type
默认为application/x-www-form-urlencoded
.
如有必要,Content-Type
也可以显式设置。
HTTP 接口
Spring Framework 允许您将 HTTP 服务定义为带有注释的 Java 接口 方法进行 HTTP 交换。然后,您可以生成实现此接口的代理 并执行交换。这有助于简化 HTTP 远程访问,这通常 涉及一个 Facade,该 Facade 包装了使用底层 HTTP 客户端的详细信息。
一、使用@HttpExchange
方法:
interface RepositoryService {
@GetExchange("/repos/{owner}/{repo}")
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
// more HTTP exchange methods...
}
第二,创建一个代理,该代理将执行声明的 HTTP 交换:
WebClient client = WebClient.builder().baseUrl("https://api.github.com/").build();
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).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 交换方法支持灵活的方法签名,包括以下内容 方法参数:
Method 参数 | 描述 |
---|---|
|
动态设置请求的 URL,覆盖注释的 |
|
动态设置请求的 HTTP 方法,覆盖注释的 |
|
添加一个或多个请求标头。参数可以是 |
|
在请求 URL 中添加用于扩展占位符的变量。参数可以是 |
|
将请求的主体作为要序列化的对象或
反应式流 |
|
添加一个或多个请求参数。参数可以是 什么时候 |
|
添加请求部分,可以是 String (表单字段), |
|
添加一个或多个 Cookie。参数可以是 |
返回值
带注释的 HTTP 交换方法支持以下返回值:
方法返回值 | 描述 |
---|---|
|
执行给定的请求,并发布响应内容(如果有)。 |
|
执行给定的请求,释放响应内容(如果有),并返回 响应标头。 |
|
执行给定的请求并将响应内容解码为声明的返回类型。 |
|
执行给定的请求,并将响应内容解码为声明的 元素类型。 |
|
执行给定的请求,并释放响应内容(如果有),并返回一个 |
|
执行给定的请求,将响应内容解码为声明的返回类型,然后
返回一个 |
|
执行给定的请求,将响应内容解码为声明的
元素类型,并返回一个 |
您还可以使用在ReactiveAdapterRegistry . |
异常处理
默认情况下,WebClient
提高WebClientResponseException
对于 4xx 和 5xx HTTP 状态
代码。要自定义此功能,您可以注册一个适用于所有
通过客户端执行的响应:
WebClient webClient = WebClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
.build();
WebClientAdapter clientAdapter = WebClientAdapter.forClient(webClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory
.builder(clientAdapter).build();
有关更多详细信息和选项(例如禁止显示错误状态代码),请参阅 JavadocdefaultStatusHandler
在WebClient.Builder
.