請求 Body

請求 body 可以從 ReactiveAdapterRegistry 處理的任何非同步類型進行編碼,例如 Mono 或 Kotlin Coroutines Deferred,如下例所示

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

您也可以將物件串流編碼,如下例所示

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

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

表單資料

若要傳送表單資料,您可以提供 MultiValueMap<String, String> 作為 body。請注意,內容會由 FormHttpMessageWriter 自動設定為 application/x-www-form-urlencoded。以下範例示範如何使用 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 以內嵌方式提供表單資料,如下例所示

  • 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 資料

若要傳送 multipart 資料,您需要提供 MultiValueMap<String, ?>,其值可以是代表 part 內容的 Object 實例,也可以是代表 part 的內容和標頭的 HttpEntity 實例。MultipartBodyBuilder 提供方便的 API 來準備 multipart 請求。以下範例示範如何建立 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()

在大多數情況下,您不必為每個 part 指定 Content-Type。內容類型會根據選擇用於序列化它的 HttpMessageWriter 自動決定,或者在 Resource 的情況下,根據檔案副檔名決定。如有必要,您可以透過其中一個多載的 builder part 方法明確提供每個 part 要使用的 MediaType

準備好 MultiValueMap 後,將其傳遞給 WebClient 最簡單的方法是透過 body 方法,如下例所示

  • 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 包裝器。

作為 MultipartBodyBuilder 的替代方案,您也可以透過內建的 BodyInserters 以內嵌樣式提供 multipart 內容,如下例所示

  • 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

若要循序串流 multipart 資料,您可以透過 PartEvent 物件提供 multipart 內容。

  • 表單欄位可以使用 FormPartEvent::create 建立。

  • 檔案上傳可以使用 FilePartEvent::create 建立。

您可以串連從方法傳回的串流,透過 Flux::concat,並為 WebClient 建立請求。

例如,此範例將 POST 包含表單欄位和檔案的 multipart 表單。

  • 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 轉發到另一個服務。