Spring/WebClient

[WebClient] RequestBody

blockbuddy93 2024. 3. 28. 14:16

RequestBody는 다음 예제와 같이 Mono나 Kotlin의 Deferred와 같이 ReactiveAdapterRegistry에서 처리되는 비동기 형식에서 인코딩될 수 있습니다.

Mono<Person> personMono = ... ;

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

아래 예제와 같이 객체의 스트림을 인코딩할 수도 있습니다.

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

또는 실제 값이 있는 경우, bodyValue 단축 메서드를 사용할 수 있습니다.

Person person = ... ;

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

 

Form Data

Form Data 를 보내려면 MultiValueMap<String, String>을 본문으로 제공하면 됩니다. 내용은 FormHttpMessageWriter에 의해 자동으로 application/x-www-form-urlencoded로 설정됩니다. MultiValueMap<String, String>을 사용하는 방법은 다음과 같습니다.

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

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

또한 BodyInserters를 사용하여 폼 데이터를 인라인으로 제공할 수 있습니다.

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

 

Multipart Data

Multipart Data 를 보내려면 값을 나타내는 개체 또는 부분 콘텐츠를 나타내는 HttpEntity 인스턴스 중 하나로 구성된 MultiValueMap<String, ?>를 제공해야 합니다. MultipartBodyBuilder는 멀티파트 요청을 준비하기 위한 편리한 API를 제공합니다. MultiValueMap<String, ?>을 생성하는 방법은 다음과 같습니다.

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); // 서버 요청에서 받은 부분

MultiValueMap<String, HttpEntity<?>> parts = builder.build();

대부분의 경우, 각 부분에 대해 Content-Type을 명시할 필요가 없습니다. 콘텐츠 유형은 해당 부분을 직렬화하기 위해 선택된 HttpMessageWriter에 따라 자동으로 결정됩니다. 또는 Resource의 경우 파일 확장자에 따라 결정됩니다. 필요한 경우, 오버로드된 빌더 부분 메서드 중 하나를 통해 각 부분에 대한 사용할 MediaType를 명시적으로 제공할 수 있습니다.

준비된 MultiValueMap이 있다면, WebClient에 전달하는 가장 쉬운 방법은 다음 예제와 같이 body 메서드를 통해 전달하는 것입니다:

MultipartBodyBuilder builder = ...;

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


MultiValueMap에 적어도 하나의 문자열이 아닌 값이 포함되어 있으면, 이 값은 일반적인 폼 데이터(application/x-www-form-urlencoded)를 나타낼 수 있으며, 따라서 Content-Type을 multipart/form-data로 설정할 필요가 없습니다. 이것은 항상 MultipartBodyBuilder를 사용할 때의 경우입니다. 

MultipartBodyBuilder 대신에 BodyInserters를 사용하여 내장된 멀티파트 콘텐츠를 인라인으로 제공할 수도 있습니다.

MultipartBodyBuilder builder = ...;

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

 

 

1. PartEvent

멀티파트 데이터를 순차적으로 스트리밍하려면 PartEvent 개체를 통해 멀티파트 콘텐츠를 제공할 수 있습니다.

  • 폼 필드는 FormPartEvent::create를 통해 생성할 수 있습니다.
  • 파일 업로드는 FilePartEvent::create를 통해 생성할 수 있습니다.

메서드에서 반환된 스트림을 Flux::concat을 통해 연결하고 WebClient에 요청을 만들 수 있습니다.

예를 들어, 다음 예제는 폼 필드와 파일을 포함하는 멀티파트 폼을 POST합니다.

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

서버 측에서 @RequestBody나 ServerRequest::bodyToFlux(PartEvent.class)를 통해 받은 PartEvent 개체는 다른 서비스를 통해 WebClient를 통해 전달할 수 있습니다.