Request Body

可以从 ReactiveAdapterRegistry 处理的任何异步类型对请求正文进行编码,如同以下示例所示:

The request body can be encoded from any asynchronous type handled by ReactiveAdapterRegistry, like Mono or Kotlin Coroutines Deferred as the following example shows:

  • Java

  • Kotlin

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

如以下示例所示,您也可以对对象的流进行编码:

You can also have a stream of objects be encoded, as the following example shows:

  • Java

  • Kotlin

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 快捷方法,如同以下示例所示:

Alternatively, if you have the actual value, you can use the bodyValue shortcut method, as the following example shows:

  • Java

  • Kotlin

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

Form Data

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

To send form data, you can provide a MultiValueMap<String, String> as the body. Note that the content is automatically set to application/x-www-form-urlencoded by the FormHttpMessageWriter. The following example shows how to use MultiValueMap<String, String>:

  • Java

  • Kotlin

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 内联提供表单数据:

You can also supply form data in-line by using BodyInserters, as the following example shows:

  • Java

  • Kotlin

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

Multipart Data

要发送多部分数据,您需要提供一个 MultiValueMap<String, ?>,其值为表示部件内容的 Object 实例,或表示部件的内容和头的 HttpEntity 实例。MultipartBodyBuilder 提供了一个便捷的 API 来准备一个多部分请求。以下示例展示如何创建一个 MultiValueMap<String, ?>

To send multipart data, you need to provide a MultiValueMap<String, ?> whose values are either Object instances that represent part content or HttpEntity instances that represent the content and headers for a part. MultipartBodyBuilder provides a convenient API to prepare a multipart request. The following example shows how to create a MultiValueMap<String, ?>:

  • Java

  • Kotlin

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 方法为每个部件明确提供要使用的 MediaType

In most cases, you do not have to specify the Content-Type for each part. The content type is determined automatically based on the HttpMessageWriter chosen to serialize it or, in the case of a Resource, based on the file extension. If necessary, you can explicitly provide the MediaType to use for each part through one of the overloaded builder part methods.

准备 MultiValueMap 后,最简单的传递给 WebClient 的方式是通过 body 方法,如同以下示例所示:

Once a MultiValueMap is prepared, the easiest way to pass it to the WebClient is through the body method, as the following example shows:

  • Java

  • Kotlin

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 包装器。

If the MultiValueMap contains at least one non-String value, which could also represent regular form data (that is, application/x-www-form-urlencoded), you need not set the Content-Type to multipart/form-data. This is always the case when using MultipartBodyBuilder, which ensures an HttpEntity wrapper.

MultipartBodyBuilder 作为替代方案,您也可以通过内置 BodyInserters 以内联样式提供多部分内容,如同以下示例所示:

As an alternative to MultipartBodyBuilder, you can also provide multipart content, inline-style, through the built-in BodyInserters, as the following example shows:

  • Java

  • Kotlin

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 对象提供多部分内容。

To stream multipart data sequentially, you can provide multipart content through PartEvent objects.

  • Form fields can be created via FormPartEvent::create.

  • File uploads can be created via FilePartEvent::create.

您可以通过 Flux::concat 连接方法返回的流,并为 WebClient 创建一个请求。

You can concatenate the streams returned from methods via Flux::concat, and create a request for the WebClient.

例如,此示例将发布包含一个表单字段和一个文件的 multipart 表单。

For instance, this sample will POST a multipart form containing a form field and a file.

  • Java

  • Kotlin

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 中继到另一个服务。

On the server side, PartEvent objects that are received via @RequestBody or ServerRequest::bodyToFlux(PartEvent.class) can be relayed to another service via the WebClient.