对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

REST 客户端

Spring 框架提供了以下几种方式用于调用 REST 端点:spring-doc.cadn.net.cn

WebClient

WebClient 是一个用于执行 HTTP 请求的非阻塞、响应式客户端。它在 5.0 版本中引入,作为 RestTemplate 的替代方案,支持同步、异步和流式处理场景。spring-doc.cadn.net.cn

WebClient 支持以下功能:spring-doc.cadn.net.cn

有关更多详情,请参见WebClientspring-doc.cadn.net.cn

RestTemplate

RestTemplate 在 HTTP 客户端库之上提供了更高层次的 API。它使得只需一行代码即可轻松调用 REST 端点。它提供了以下几组重载方法:spring-doc.cadn.net.cn

RestTemplate 已进入维护模式,仅接受针对微小变更和 bug 的请求。请考虑改用 WebClient
表1. RestTemplate 方法
方法组 <description> </description>

getForObjectspring-doc.cadn.net.cn

通过 GET 请求获取一个表示。spring-doc.cadn.net.cn

getForEntityspring-doc.cadn.net.cn

使用 GET 方法获取一个 ResponseEntity(即状态、头部和主体)。spring-doc.cadn.net.cn

headForHeadersspring-doc.cadn.net.cn

通过使用 HEAD 方法检索资源的所有标头。spring-doc.cadn.net.cn

postForLocationspring-doc.cadn.net.cn

通过使用 POST 创建一个新资源,并从响应中返回 Location 头。spring-doc.cadn.net.cn

postForObjectspring-doc.cadn.net.cn

通过使用 POST 创建一个新资源,并返回响应中的表示形式。spring-doc.cadn.net.cn

postForEntityspring-doc.cadn.net.cn

通过使用 POST 创建一个新资源,并返回响应中的表示形式。spring-doc.cadn.net.cn

putspring-doc.cadn.net.cn

通过使用 PUT 创建或更新资源。spring-doc.cadn.net.cn

patchForObjectspring-doc.cadn.net.cn

通过使用 PATCH 方法更新资源,并返回响应中的资源表示。 请注意,JDK 的 HttpURLConnection 不支持 PATCH 方法,但 Apache HttpComponents 等其他库支持。spring-doc.cadn.net.cn

deletespring-doc.cadn.net.cn

使用 DELETE 方法删除指定 URI 处的资源。spring-doc.cadn.net.cn

optionsForAllowspring-doc.cadn.net.cn

通过使用 ALLOW 方法检索资源所允许的 HTTP 方法。spring-doc.cadn.net.cn

exchangespring-doc.cadn.net.cn

前述方法的一个更为通用(且约束更少)的版本,在需要时提供了额外的灵活性。它接受一个 RequestEntity(包含 HTTP 方法、URL、请求头和请求体作为输入),并返回一个 ResponseEntityspring-doc.cadn.net.cn

这些方法允许使用 ParameterizedTypeReference 而非 Class 来指定带有泛型的响应类型。spring-doc.cadn.net.cn

executespring-doc.cadn.net.cn

通过回调接口对请求准备和响应提取进行完全控制,从而以最通用的方式执行请求。spring-doc.cadn.net.cn

初始化

默认构造函数使用 java.net.HttpURLConnection 来执行请求。你可以通过实现 ClientHttpRequestFactory 接口来切换到其他 HTTP 库。 目前,还内置支持 Apache HttpComponents 和 OkHttp。spring-doc.cadn.net.cn

例如,要切换到 Apache HttpComponents,您可以使用以下方式:spring-doc.cadn.net.cn

RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());

每个 ClientHttpRequestFactory 都会暴露底层 HTTP 客户端库特有的配置选项——例如,用于凭证、连接池以及其他细节。spring-doc.cadn.net.cn

请注意,当访问表示错误(例如 401)的响应状态时,java.net 的 HTTP 请求实现可能会抛出异常。如果这会造成问题,请切换到其他 HTTP 客户端库。
RestTemplate 可以进行可观测性(observability)插桩,以生成指标(metrics)和追踪(traces)。 参见RestTemplate 可观测性支持一节。

统一资源标识符(URIs)

许多 RestTemplate 方法接受一个 URI 模板和 URI 模板变量,这些变量可以作为 String 可变参数传入,也可以作为 Map<String,String> 传入。spring-doc.cadn.net.cn

以下示例使用了一个 String 可变参数:spring-doc.cadn.net.cn

String result = restTemplate.getForObject(
		"https://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");

以下示例使用了一个 Map<String, String>spring-doc.cadn.net.cn

Map<String, String> vars = Collections.singletonMap("hotel", "42");

String result = restTemplate.getForObject(
		"https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);

请记住,URI 模板会自动进行编码,如下例所示:spring-doc.cadn.net.cn

restTemplate.getForObject("https://example.com/hotel list", String.class);

// Results in request to "https://example.com/hotel%20list"

您可以使用 uriTemplateHandlerRestTemplate 属性来自定义 URI 的编码方式。或者,您也可以预先创建一个 java.net.URI 对象,并将其传入某个接受 RestTemplate 参数的 URI 方法中。spring-doc.cadn.net.cn

有关处理和编码 URI 的更多详细信息,请参阅URI 链接spring-doc.cadn.net.cn

headers

您可以使用 exchange() 方法来指定请求头,如下例所示:spring-doc.cadn.net.cn

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

你可以通过许多返回 RestTemplateResponseEntity 方法变体来获取响应头。spring-doc.cadn.net.cn

身体

传入和从 RestTemplate 方法返回的对象,会借助 HttpMessageConverter 转换为原始内容或从原始内容转换而来。spring-doc.cadn.net.cn

在 POST 请求中,输入对象会被序列化到请求体中,如下例所示:spring-doc.cadn.net.cn

URI location = template.postForLocation("https://example.com/people", person);

你无需显式设置请求的 Content-Type 头。在大多数情况下,你可以根据源 Object 类型找到一个兼容的消息转换器,而所选的消息转换器会相应地设置内容类型。如有必要,你可以使用 exchange 方法显式提供 Content-Type 请求头,这反过来会影响所选择的消息转换器。spring-doc.cadn.net.cn

在执行 GET 请求时,响应体将被反序列化为一个输出 Object,如下例所示:spring-doc.cadn.net.cn

Person person = restTemplate.getForObject("https://example.com/people/{id}", Person.class, 42);

请求的 Accept 头部无需显式设置。在大多数情况下,可以根据预期的响应类型找到兼容的消息转换器,该转换器随后会帮助填充 Accept 头部。如有必要,您可以使用 exchange 方法显式提供 Accept 头部。spring-doc.cadn.net.cn

默认情况下,RestTemplate 会根据类路径检查注册所有内置的 消息转换器,这些检查有助于确定哪些可选的转换库已存在。你也可以显式地设置要使用的消息转换器。spring-doc.cadn.net.cn

消息转换

spring-web 模块包含了 HttpMessageConverter 接口,用于通过 InputStreamOutputStream 读取和写入 HTTP 请求与响应的正文。 HttpMessageConverter 实例在客户端(例如,在 RestTemplate 中)和服务器端(例如,在 Spring MVC 的 REST 控制器中)都会被使用。spring-doc.cadn.net.cn

框架中提供了主要媒体(MIME)类型的具體實現,默認情況下,這些實現會在客戶端註冊到 RestTemplate,並在服務器端註冊到 RequestMappingHandlerAdapter(參見 配置消息轉換器)。spring-doc.cadn.net.cn

HttpMessageConverter 的实现将在以下各节中进行描述。 对于所有转换器,都会使用一个默认的媒体类型,但你可以通过设置 supportedMediaTypes bean 属性来覆盖它。下表描述了每种实现:spring-doc.cadn.net.cn

表2. HttpMessageConverter 实现类
消息转换器 <description> </description>

StringHttpMessageConverterspring-doc.cadn.net.cn

一种 HttpMessageConverter 实现,可以从 HTTP 请求和响应中读取和写入 String 实例。默认情况下,该转换器支持所有文本媒体类型(text/*),并以 Content-Typetext/plain 的格式进行写入。spring-doc.cadn.net.cn

FormHttpMessageConverterspring-doc.cadn.net.cn

一种 HttpMessageConverter 实现,能够从 HTTP 请求和响应中读取和写入表单数据。默认情况下,该转换器处理 application/x-www-form-urlencoded 媒体类型。表单数据从 MultiValueMap<String, String> 中读取并写入其中。该转换器还可以将从 MultiValueMap<String, Object> 读取的多部分(multipart)数据进行写入(但不能读取)。默认支持 multipart/form-data 类型。从 Spring Framework 5.2 起,写入表单数据时可以支持额外的 multipart 子类型。更多详细信息,请参阅 FormHttpMessageConverter 的 Javadoc。spring-doc.cadn.net.cn

ByteArrayHttpMessageConverterspring-doc.cadn.net.cn

一种 HttpMessageConverter 实现,能够从 HTTP 请求和响应中读取和写入字节数组。默认情况下,此转换器支持所有媒体类型(*/*),并以 Content-Typeapplication/octet-stream 进行写入。您可以通过设置 supportedMediaTypes 属性并重写 getContentType(byte[]) 方法来覆盖此行为。spring-doc.cadn.net.cn

MarshallingHttpMessageConverterspring-doc.cadn.net.cn

一种 HttpMessageConverter 实现,它通过使用 Spring 的 Marshaller 包中的 Unmarshallerorg.springframework.oxm 抽象来读取和写入 XML。 此转换器在使用前需要一个 MarshallerUnmarshaller。您可以通过构造函数或 Bean 属性注入这些组件。默认情况下,该转换器支持 text/xmlapplication/xmlspring-doc.cadn.net.cn

MappingJackson2HttpMessageConverterspring-doc.cadn.net.cn

一种 HttpMessageConverter 实现,它使用 Jackson 的 ObjectMapper 来读取和写入 JSON。你可以通过使用 Jackson 提供的注解,按需自定义 JSON 映射。当你需要更精细的控制时(例如,需要为特定类型提供自定义的 JSON 序列化器/反序列化器),可以通过 ObjectMapper 属性注入一个自定义的 ObjectMapper。默认情况下,该转换器支持 application/jsonspring-doc.cadn.net.cn

MappingJackson2XmlHttpMessageConverterspring-doc.cadn.net.cn

一种 HttpMessageConverter 实现,它通过使用 Jackson XML 扩展的 XmlMapper 来读取和写入 XML。你可以通过 JAXB 或 Jackson 提供的注解按需自定义 XML 映射。当你需要更精细的控制时(例如,需要为特定类型提供自定义的 XML 序列化器/反序列化器),可以通过 XmlMapper 属性注入一个自定义的 ObjectMapper。默认情况下,该转换器支持 application/xmlspring-doc.cadn.net.cn

SourceHttpMessageConverterspring-doc.cadn.net.cn

一种 HttpMessageConverter 实现,能够从 HTTP 请求和响应中读取和写入 javax.xml.transform.Source。仅支持 DOMSourceSAXSourceStreamSource。默认情况下,此转换器支持 text/xmlapplication/xmlspring-doc.cadn.net.cn

BufferedImageHttpMessageConverterspring-doc.cadn.net.cn

一种 HttpMessageConverter 实现,能够从 HTTP 请求和响应中读取和写入 java.awt.image.BufferedImage。该转换器读取和写入 Java I/O API 所支持的媒体类型。spring-doc.cadn.net.cn

Jackson JSON 视图

您可以指定一个Jackson JSON 视图, 以仅序列化对象属性的一个子集,如下例所示:spring-doc.cadn.net.cn

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

多部分(Multipart)

要发送 multipart 数据,您需要提供一个 MultiValueMap<String, Object>,其值可以是用于部分(part)内容的 Object、用于文件部分的 Resource,或用于带有头部信息的部分内容的 HttpEntity。例如:spring-doc.cadn.net.cn

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。内容类型会根据用于序列化该部分的 HttpMessageConverter 自动确定,或者在基于 Resource 的情况下,根据文件扩展名自动确定。如有必要,您可以使用 MediaType 包装器显式提供 HttpEntityspring-doc.cadn.net.cn

一旦 MultiValueMap 准备就绪,你就可以将其传递给 RestTemplate,如下所示:spring-doc.cadn.net.cn

MultiValueMap<String, Object> parts = ...;
template.postForObject("https://example.com/upload", parts, Void.class);

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

HTTP 接口

Spring 框架允许你将 HTTP 服务定义为一个 Java 接口,其中使用注解标注的方法用于处理 HTTP 通信。然后,你可以生成一个代理来实现该接口并执行这些通信操作。这有助于简化 HTTP 远程访问,而此类访问通常涉及一个外观(facade),用于封装底层 HTTP 客户端的使用细节。spring-doc.cadn.net.cn

首先,声明一个包含 @HttpExchange 方法的接口:spring-doc.cadn.net.cn

interface RepositoryService {

	@GetExchange("/repos/{owner}/{repo}")
	Repository getRepository(@PathVariable String owner, @PathVariable String repo);

	// more HTTP exchange methods...

}

其次,创建一个代理,用于执行所声明的 HTTP 交换:spring-doc.cadn.net.cn

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 在类型级别上受支持,适用于所有方法:spring-doc.cadn.net.cn

@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 交换方法支持灵活的方法签名,可使用以下方法参数:spring-doc.cadn.net.cn

方法参数 <description> </description>

URIspring-doc.cadn.net.cn

动态设置请求的 URL,覆盖注解中的 url 属性。spring-doc.cadn.net.cn

HttpMethodspring-doc.cadn.net.cn

动态设置请求的 HTTP 方法,覆盖注解中的 method 属性spring-doc.cadn.net.cn

@RequestHeaderspring-doc.cadn.net.cn

添加一个请求头或多个请求头。参数可以是包含多个请求头的 Map<String, ?>MultiValueMap<String, ?>,也可以是值的 Collection<?>,或者单个值。 对于非 String 类型的值,支持类型转换。spring-doc.cadn.net.cn

@PathVariablespring-doc.cadn.net.cn

添加一个变量,用于在请求 URL 中展开占位符。该参数可以是一个包含多个变量的 Map<String, ?>,也可以是单个值。对于非 String 类型的值,支持类型转换。spring-doc.cadn.net.cn

@RequestBodyspring-doc.cadn.net.cn

以待序列化的对象形式提供请求体,或者以响应式流(Reactive Streams)Publisher 的形式提供,例如 MonoFlux,或通过已配置的 ReactiveAdapterRegistry 所支持的任何其他异步类型。spring-doc.cadn.net.cn

@RequestParamspring-doc.cadn.net.cn

添加一个或多个请求参数。参数可以是包含多个参数的 Map<String, ?>MultiValueMap<String, ?>,也可以是值的 Collection<?>,或者单个值。对于非 String 类型的值,支持类型转换。spring-doc.cadn.net.cn

"content-type" 设置为 "application/x-www-form-urlencoded" 时,请求参数会被编码在请求体中。否则,它们将作为 URL 查询参数添加。spring-doc.cadn.net.cn

@RequestPartspring-doc.cadn.net.cn

添加一个请求部分,该部分可以是 String(表单字段)、Resource(文件部分)、 Object(要编码的实体,例如 JSON 格式)、HttpEntity(部分内容和头部信息)、 Spring 的 Part,或者上述任意类型的 Reactive Streams Publisherspring-doc.cadn.net.cn

@CookieValuespring-doc.cadn.net.cn

添加一个或多个 Cookie。参数可以是一个包含多个 Cookie 的 Map<String, ?>MultiValueMap<String, ?>,也可以是一个值的 Collection<?>,或者单个值。 对于非 String 类型的值,支持类型转换。spring-doc.cadn.net.cn

返回值

带注解的 HTTP 交换方法支持以下返回值:spring-doc.cadn.net.cn

方法返回值 <description> </description>

void, Mono<Void>spring-doc.cadn.net.cn

执行给定的请求,并释放响应内容(如果有的话)。spring-doc.cadn.net.cn

HttpHeaders, Mono<HttpHeaders>spring-doc.cadn.net.cn

执行给定的请求,释放响应内容(如果有的话),并返回响应头。spring-doc.cadn.net.cn

<T>, Mono<T>spring-doc.cadn.net.cn

执行给定的请求,并将响应内容解码为声明的返回类型。spring-doc.cadn.net.cn

<T>, Flux<T>spring-doc.cadn.net.cn

执行给定的请求,并将响应内容解码为所声明元素类型的流。spring-doc.cadn.net.cn

ResponseEntity<Void>, Mono<ResponseEntity<Void>>spring-doc.cadn.net.cn

执行给定的请求,释放响应内容(如果有的话),并返回一个包含状态和头部信息的ResponseEntityspring-doc.cadn.net.cn

ResponseEntity<T>, Mono<ResponseEntity<T>>spring-doc.cadn.net.cn

执行给定的请求,将响应内容解码为声明的返回类型,并返回一个包含状态、头部信息和解码后响应体的 ResponseEntityspring-doc.cadn.net.cn

Mono<ResponseEntity<Flux<T>>spring-doc.cadn.net.cn

执行给定的请求,将响应内容解码为声明元素类型的流,并返回一个包含状态、头部信息和已解码响应体流的 ResponseEntityspring-doc.cadn.net.cn

你也可以使用在 ReactiveAdapterRegistry 中注册的任何其他异步或响应式类型。

异常处理

默认情况下,WebClient 在遇到 4xx 和 5xx HTTP 状态码时会抛出 WebClientResponseException 异常。要自定义此行为,您可以注册一个响应状态处理器,该处理器将应用于通过该客户端执行的所有响应:spring-doc.cadn.net.cn

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

WebClientAdapter clientAdapter = WebClientAdapter.forClient(webClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory
		.builder(clientAdapter).build();

有关更多详细信息和选项(例如抑制错误状态码),请参见 defaultStatusHandlerWebClient.Builder 的 Javadoc。spring-doc.cadn.net.cn