반응형

다음의 예와 같이, 요청을 인터셉트하여 변경하기 위해 WebClient.Builder을 통해 클라이언트 필터 (ExchangeFilterFunction)을 등록 할 수 있다.

Java

WebClient client = WebClient.builder()
        .filter((request, next) -> {

            ClientRequest filtered = ClientRequest.from(request)
                    .header("foo", "bar")
                    .build();

            return next.exchange(filtered);
        })
        .build();

Kotlin

val client = WebClient.builder()
        .filter { request, next ->

            val filtered = ClientRequest.from(request)
                    .header("foo", "bar")
                    .build()

            next.exchange(filtered)
        }
        .build()

이것은 인증 등의 횡단적 관심사( cross-cutting concerns)에 사용할 수 있다. 다음 예제에서는 정적 팩토리 방식을 통한 기본 인증 필터를 사용하고 있다.

Java

import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;

WebClient client = WebClient.builder()
        .filter(basicAuthentication("user", "password"))
        .build();

Kotlin

import org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication

val client = WebClient.builder()
        .filter(basicAuthentication("user", "password"))
        .build()

다른 인스턴스를 시작점으로 사용하여 새로운 WebClient 인스턴스를 만들 수 있다. 그러면 원본 WebClient에 영향을 주지 않고 필터를 삽입 또는 삭제할 수 있다. 다음은 인덱스 0에 기본 인증 필터를 삽입하는 예이다.

Java

import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;

WebClient client = webClient.mutate()
        .filters(filterList -> {
            filterList.add(0, basicAuthentication("user", "password"));
        })
        .build();

Kotlin

val client = webClient.mutate()
        .filters { it.add(0, basicAuthentication("user", "password")) }
        .build()
반응형
반응형

요청 본문은 다음 예와 같이 Mono 나 Kotlin 코루틴(Coroutines) DeferredReactiveAdapterRegistry에 의해 처리되는 모든 비동기 유형에서 인코딩 할 수 있다.

Java

Mono<Person> personMono = ... ;

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

Kotlin

val personDeferred: Deferred<Person> = ...

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

다음의 예와 같이 객체 스트림을 인코딩 할 수 있다.

Java

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

Kotlin

val people: Flow<Person> = ...

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

또는 실제 값이 있는 경우는 다음의 예와 같이, 간단히 bodyValue 메소드를 사용할 수 있다.

Java

Person person = ... ;

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

Kotlin

val person: Person = ...

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

2.4.1. 폼 데이터(Form Data)

폼 데이터를 전송하려면 MultiValueMap<String, String> 을 본문으로 제공 할 수 있다. 콘텐츠는 FormHttpMessageWriter에 의해 application/x-www-form-urlencoded으로 자동 설정되는 것에 주의해라. 다음 예제는 MultiValueMap<String, String>사용 방법을 보여준다.

Java

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

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

Kotlin

val formData: MultiValueMap<String, String> = ...

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

다음의 예와 같이, BodyInserters을 사용하여 인라인 폼 데이터를 제공 할 수 있다.

Java

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

Kotlin

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

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

2.4.2. 멀티파트 데이터(Multipart Data)

멀티파트 데이터를 전송하려면, 파트 콘텐츠(part content)를 나타내는 Object 인스턴스 또는 파트 컨텐츠와 헤더를 나타내는 HttpEntity 인스턴스 중 하나인 MultiValueMap<String, ?> 를 제공해야 한다. MultipartBodyBuilder는 멀티 파트 요청을 준비하기 위한 편리한 API를 제공한다. 다음의 예는 MultiValueMap<String, ?>을 만드는 방법을 보여준다.

Java

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

Kotlin

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

val parts = builder.build()

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

MultiValueMap가 준비되면 다음의 예와 같이, WebClient에 전달하는 가장 쉬운 방법은 body 메소드를 사용하는 것이다.

Java

MultipartBodyBuilder builder = ...;

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

Kotlin

val builder: MultipartBodyBuilder = ...

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

MultiValueMap에 적어도 하나의 String 다른 값(보통 폼 데이터. 즉, application/x-www-form-urlencoded를 나타내는 경우도 있다)가 포함되어 있는 경우, Content-Typemultipart/form-data로 설정할 필요는 없다. 이것은 MultipartBodyBuilder을 사용하는 경우 항상 적용, HttpEntity 래퍼가 보장된다.

MultipartBodyBuilder의 대안으로 다음의 예와 같이 내장의 BodyInserters를 통해 인라인 스타일의 멀티 파트 콘텐츠를 제공 할 수 있다.

Java

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

Kotlin

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

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

client.post()

요청 본문은 다음 예와 같이 Mono 나 Kotlin 코루틴(Coroutines) DeferredReactiveAdapterRegistry에 의해 처리되는 모든 비동기 유형에서 인코딩 할 수 있다.

Java

Mono<Person> personMono = ... ;

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

Kotlin

val personDeferred: Deferred<Person> = ...

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

다음의 예와 같이 객체 스트림을 인코딩 할 수 있다.

Java

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

Kotlin

val people: Flow<Person> = ...

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

또는 실제 값이 있는 경우는 다음의 예와 같이, 간단히 bodyValue 메소드를 사용할 수 있다.

Java

Person person = ... ;

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

Kotlin

val person: Person = ...

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

2.4.1. 폼 데이터(Form Data)

폼 데이터를 전송하려면 MultiValueMap<String, String> 을 본문으로 제공 할 수 있다. 콘텐츠는 FormHttpMessageWriter에 의해 application/x-www-form-urlencoded으로 자동 설정되는 것에 주의해라. 다음 예제는 MultiValueMap<String, String>사용 방법을 보여준다.

Java

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

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

Kotlin

val formData: MultiValueMap<String, String> = ...

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

다음의 예와 같이, BodyInserters을 사용하여 인라인 폼 데이터를 제공 할 수 있다.

Java

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

Kotlin

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

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

2.4.2. 멀티파트 데이터(Multipart Data)

멀티파트 데이터를 전송하려면, 파트 콘텐츠(part content)를 나타내는 Object 인스턴스 또는 파트 컨텐츠와 헤더를 나타내는 HttpEntity 인스턴스 중 하나인 MultiValueMap<String, ?> 를 제공해야 한다. MultipartBodyBuilder는 멀티 파트 요청을 준비하기 위한 편리한 API를 제공한다. 다음의 예는 MultiValueMap<String, ?>을 만드는 방법을 보여준다.

Java

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

Kotlin

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

val parts = builder.build()

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

MultiValueMap가 준비되면 다음의 예와 같이, WebClient에 전달하는 가장 쉬운 방법은 body 메소드를 사용하는 것이다.

Java

MultipartBodyBuilder builder = ...;

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

Kotlin

val builder: MultipartBodyBuilder = ...

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

MultiValueMap에 적어도 하나의 String 다른 값(보통 폼 데이터. 즉, application/x-www-form-urlencoded를 나타내는 경우도 있다)가 포함되어 있는 경우, Content-Typemultipart/form-data로 설정할 필요는 없다. 이것은 MultipartBodyBuilder을 사용하는 경우 항상 적용, HttpEntity 래퍼가 보장된다.

MultipartBodyBuilder의 대안으로 다음의 예와 같이 내장의 BodyInserters를 통해 인라인 스타일의 멀티 파트 콘텐츠를 제공 할 수 있다.

Java

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

Kotlin

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

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

exchangeToMono() 메소드와 exchangeToFlux() 메소드 (또는 Kotlin에서 awaitExchange { }exchangeToFlow { })는 응답 상태에 따라 다르게 응답을 디코딩하는 등의 고급 제어가 필요한 고급 케이스에 도움이 된다.

Java

Mono<Object> entityMono = client.get()
        .uri("/persons/1")
        .accept(MediaType.APPLICATION_JSON)
        .exchangeToMono(response -> {
            if (response.statusCode().equals(HttpStatus.OK)) {
                return response.bodyToMono(Person.class);
            }
            else if (response.statusCode().is4xxClientError()) {
                // Suppress error status code
                return response.bodyToMono(ErrorContainer.class);
            }
            else {
                // Turn to error
                return response.createException().flatMap(Mono::error);
            }
        });

Kotlin

val entity = client.get()
  .uri("/persons/1")
  .accept(MediaType.APPLICATION_JSON)
  .awaitExchange {
        if (response.statusCode() == HttpStatus.OK) {
             return response.awaitBody<Person>()
        }
        else if (response.statusCode().is4xxClientError) {
             return response.awaitBody<ErrorContainer>()
        }
        else {
             throw response.createExceptionAndAwait()
        }
  }

위를 사용하는 경우, 반환된 Mono 또는 Flux가 완료된 후에, 응답 본문을 확인하고 사용되지 않았으면 메모리와 연결 누수가 방지하기 위해 해제된다. 따라서 응답은 더 이상 다운스트림으로 디코딩 할 수 없다. 필요에 따라 응답을 해독하는 방법을 선언하는 것은 제공된 함수 달려 있다.

반응형
반응형

retrieve() 메소드를 사용하여, 응답을 추출하는 방법을 선언 할 수 있다. 예를 들면 아래와 같다.

Java

WebClient client = WebClient.create("https://example.org");

Mono<ResponseEntity<Person>> result = client.get()
        .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .toEntity(Person.class);

Kotlin

val client = WebClient.create("https://example.org")

val result = client.get()
        .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .toEntity<Person>().awaitSingle()

또는 body만 얻으려면 :

Java

WebClient client = WebClient.create("https://example.org");

Mono<Person> result = client.get()
        .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .bodyToMono(Person.class);

Kotlin

val client = WebClient.create("https://example.org")

val result = client.get()
        .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .awaitBody<Person>()

디코딩된 객체 스트림을 얻으려면 :

Java

Flux<Quote> result = client.get()
        .uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
        .retrieve()
        .bodyToFlux(Quote.class);

Kotlin

val result = client.get()
        .uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
        .retrieve()
        .bodyToFlow<Quote>()

기본적으로 4xx 또는 5xx 응답은 특정 HTTP 상태 코드의 서브 클래스를 포함하는 WebClientResponseException이 된다. 오류 응답 처리를 사용자 정의하려면 다음과 같이 onStatus 핸들러를 사용한다.

Java

Mono<Person> result = client.get()
        .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .onStatus(HttpStatus::is4xxClientError, response -> ...)
        .onStatus(HttpStatus::is5xxServerError, response -> ...)
        .bodyToMono(Person.class);

Kotlin

val result = client.get()
        .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .onStatus(HttpStatus::is4xxClientError) { ... }
        .onStatus(HttpStatus::is5xxServerError) { ... }
        .awaitBody<Person>()
반응형

+ Recent posts