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

请求体

请求正文可以从任何由ReactiveAdapterRegistry处理的异步类型进行编码, 如Mono或Kotlin协程Deferred,如下例所示:spring-doc.cadn.net.cn

Mono<Person> personMono = ... ;

Mono<Void> result = client.post()
		.uri("/persons/{id}", id)
		.contentType(MediaType.APPLICATION_JSON)
		.body(personMono, Person.class)
		.retrieve()
		.bodyToMono(Void.class);
val personDeferred: Deferred<Person> = ...

client.post()
		.uri("/persons/{id}", id)
		.contentType(MediaType.APPLICATION_JSON)
		.body<Person>(personDeferred)
		.retrieve()
		.awaitBody<Unit>()

你也可以将对象流进行编码,如下例所示:spring-doc.cadn.net.cn

Flux<Person> personFlux = ... ;

Mono<Void> result = client.post()
		.uri("/persons/{id}", id)
		.contentType(MediaType.APPLICATION_STREAM_JSON)
		.body(personFlux, Person.class)
		.retrieve()
		.bodyToMono(Void.class);
val people: Flow<Person> = ...

client.post()
		.uri("/persons/{id}", id)
		.contentType(MediaType.APPLICATION_JSON)
		.body(people)
		.retrieve()
		.awaitBody<Unit>()

或者,如果您有实际值,可以使用bodyValue快捷方法, 如下例所示:spring-doc.cadn.net.cn

Person person = ... ;

Mono<Void> result = client.post()
		.uri("/persons/{id}", id)
		.contentType(MediaType.APPLICATION_JSON)
		.bodyValue(person)
		.retrieve()
		.bodyToMono(Void.class);
val person: Person = ...

client.post()
		.uri("/persons/{id}", id)
		.contentType(MediaType.APPLICATION_JSON)
		.bodyValue(person)
		.retrieve()
		.awaitBody<Unit>()

表单数据

要发送表单数据,你可以提供一个 MultiValueMap<String, String> 作为正文。请注意,内容会由 FormHttpMessageWriter 自动设置为 application/x-www-form-urlencoded。以下示例展示了如何使用 MultiValueMap<String, String>spring-doc.cadn.net.cn

MultiValueMap<String, String> formData = ... ;

Mono<Void> result = client.post()
		.uri("/path", id)
		.bodyValue(formData)
		.retrieve()
		.bodyToMono(Void.class);
val formData: MultiValueMap<String, String> = ...

client.post()
		.uri("/path", id)
		.bodyValue(formData)
		.retrieve()
		.awaitBody<Unit>()

您也可以通过使用 BodyInserters 在行内提供表单数据,如下例所示:spring-doc.cadn.net.cn

import static org.springframework.web.reactive.function.BodyInserters.*;

Mono<Void> result = client.post()
		.uri("/path", id)
		.body(fromFormData("k1", "v1").with("k2", "v2"))
		.retrieve()
		.bodyToMono(Void.class);
import org.springframework.web.reactive.function.BodyInserters.*

client.post()
		.uri("/path", id)
		.body(fromFormData("k1", "v1").with("k2", "v2"))
		.retrieve()
		.awaitBody<Unit>()

多部分数据

要发送多部分数据,你需要提供一个 MultiValueMap<String, ?>,其值要么是表示部分内容的 Object 实例,要么是表示部分的内容和头信息的 HttpEntity 实例。MultipartBodyBuilder 提供了一个方便的 API 来准备多部分请求。以下示例展示了如何创建一个 MultiValueMap<String, ?>spring-doc.cadn.net.cn

MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("fieldPart", "fieldValue");
builder.part("filePart1", new FileSystemResource("...logo.png"));
builder.part("jsonPart", new Person("Jason"));
builder.part("myPart", part); // Part from a server request

MultiValueMap<String, HttpEntity<?>> parts = builder.build();
val builder = MultipartBodyBuilder().apply {
	part("fieldPart", "fieldValue")
	part("filePart1", FileSystemResource("...logo.png"))
	part("jsonPart", Person("Jason"))
	part("myPart", part) // Part from a server request
}

val parts = builder.build()

在大多数情况下,你不必为每一部分指定Content-Type。内容类型是根据选择的HttpMessageWriter自动确定的,用于序列化它,或者在Resource的情况下,基于文件扩展名。如果必要,你可以通过其中一个重载的构建器part方法显式提供每个部分要使用的MediaTypespring-doc.cadn.net.cn

一旦准备好了MultiValueMap,将其传递给WebClient的最简单方法是通过body方法,如下例所示:spring-doc.cadn.net.cn

MultipartBodyBuilder builder = ...;

Mono<Void> result = client.post()
		.uri("/path", id)
		.body(builder.build())
		.retrieve()
		.bodyToMono(Void.class);
val builder: MultipartBodyBuilder = ...

client.post()
		.uri("/path", id)
		.body(builder.build())
		.retrieve()
		.awaitBody<Unit>()

如果 MultiValueMap 包含至少一个非-String 值,这也可以表示常规表单数据(即,application/x-www-form-urlencoded),则无需将 Content-Type 设置为 multipart/form-data。在使用 MultipartBodyBuilder 时总是这样,它确保了一个 HttpEntity 包装器。spring-doc.cadn.net.cn

作为替代方案,你也可以通过内置的BodyInserters以多部分内容、内联样式提供,如下例所示:spring-doc.cadn.net.cn

import static org.springframework.web.reactive.function.BodyInserters.*;

Mono<Void> result = client.post()
		.uri("/path", id)
		.body(fromMultipartData("fieldPart", "value").with("filePart", resource))
		.retrieve()
		.bodyToMono(Void.class);
import org.springframework.web.reactive.function.BodyInserters.*

client.post()
		.uri("/path", id)
		.body(fromMultipartData("fieldPart", "value").with("filePart", resource))
		.retrieve()
		.awaitBody<Unit>()

PartEvent

要顺序流式传输多部分数据,您可以通过 PartEvent 对象提供多部分内容。spring-doc.cadn.net.cn

您可以通过 Flux::concat 连接从方法返回的流,并为 WebClient 创建请求。spring-doc.cadn.net.cn

例如,此示例将 POST 一个包含表单字段和文件的多部分表单。spring-doc.cadn.net.cn

Resource resource = ...
Mono<String> result = webClient
    .post()
    .uri("https://example.com")
    .body(Flux.concat(
            FormPartEvent.create("field", "field value"),
            FilePartEvent.create("file", resource)
    ), PartEvent.class)
    .retrieve()
    .bodyToMono(String.class);
var resource: Resource = ...
var result: Mono<String> = webClient
	.post()
	.uri("https://example.com")
	.body(
		Flux.concat(
			FormPartEvent.create("field", "field value"),
			FilePartEvent.create("file", resource)
		)
	)
	.retrieve()
	.bodyToMono()

在服务器端,通过@RequestBodyServerRequest::bodyToFlux(PartEvent.class)接收到的PartEvent对象可以经由WebClient转发给另一个服务。spring-doc.cadn.net.cn