Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get response body when testing the status code in WebFlux WebClient?

How do you retrieve the response body when trying to throw an exception based on the returned status code? For instance, lets say I want to throw an exception and reject HTTP 201.

client.post().exchange().doOnSuccess(response -> {     if (response.statusCode().value() == 201) {         throw new RuntimeException();     } } 

How can I populate the exception with the response's body so I can throw a detailed WebClientResponseException?

Should I be using a different method to test the response status code?

edit: I am trying to duplicate the following functionality while using exchange() instead.

client.get()     .retrieve()     .onStatus(s -> !HttpStatus.CREATED.equals(s),         MyClass::createResponseException);  //MyClass public static Mono<WebClientResponseException> createResponseException(ClientResponse response) {     return response.body(BodyExtractors.toDataBuffers())             .reduce(DataBuffer::write)             .map(dataBuffer -> {                 byte[] bytes = new byte[dataBuffer.readableByteCount()];                 dataBuffer.read(bytes);                 DataBufferUtils.release(dataBuffer);                 return bytes;             })             .defaultIfEmpty(new byte[0])             .map(bodyBytes -> {                 String msg = String.format("ClientResponse has erroneous status code: %d %s", response.statusCode().value(),                         response.statusCode().getReasonPhrase());                 Charset charset = response.headers().contentType()                         .map(MimeType::getCharset)                         .orElse(StandardCharsets.ISO_8859_1);                 return new WebClientResponseException(msg,                         response.statusCode().value(),                         response.statusCode().getReasonPhrase(),                         response.headers().asHttpHeaders(),                         bodyBytes,                         charset                         );             }); } 
like image 773
CoryO Avatar asked Oct 15 '17 20:10

CoryO


People also ask

How do I log a body response in WebClient?

To log the response you can write another ExchangeFilterFunction and add it to WebClient . You can use ExchangeFilterFunction. ofResponseProcessor helper for this purpose just the same way as ExchangeFilterFunction. ofRequestProcessor is used.

How do you handle response from WebClient?

The retrieve() method in WebClient throws a WebClientResponseException whenever a response with status code 4xx or 5xx is received. You can handle the exception by checking the response status code.

What is the difference between exchange and retrieve in WebClient?

The retrieve() method decodes the ClientResponse object and hands you the ready-made object for your use. It doesn't have a very nice api for handling exceptions. However on the other hand the exchange() method hands you the ClientResponse object itself along with the response status and headers.


2 Answers

You could achieve like this by having a custom ExchangeFilterFunction and then hooking this up with WebClient.Builder before you build WebClient.

public static ExchangeFilterFunction errorHandlingFilter() {         return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {             if(clientResponse.statusCode()!=null && (clientResponse.statusCode().is5xxServerError() || clientResponse.statusCode().is4xxClientError()) ) {                  return clientResponse.bodyToMono(String.class)                          .flatMap(errorBody -> {                              return Mono.error(new CustomWebClientResponseException(errorBody,clientResponse.statusCode()));                              });             }else {                 return Mono.just(clientResponse);             }         });     } 

You can use the above like this:

WebClient.builder()                 .clientConnector(new ReactorClientHttpConnector(clientOptions))                 .defaultHeader(HttpHeaders.USER_AGENT, "Application")                 .filter(WebClientUtil.errorHandlingFilter())                 .baseUrl("https://httpbin.org/")                 .build()                 .post()                 .uri("/post")                 .body(BodyInserters.fromObject(customObjectReference) )                 .exchange()                 .flatMap(response -> response.toEntity(String.class) ); 

So any 4XX or 5XX HttpResponse will actually throw CustomWebClientResponseException and you can configure some global exception handler and do what you like to with this. Atleast using ExchangeFilterFunction you can have global place to handle things like this or add custom headers and stuff too.

like image 196
ROCKY Avatar answered Sep 30 '22 02:09

ROCKY


doOn** operators are side-effects operators and should be used for logging purposes, for example.

Here, you'd like to implement that behavior at the pipeline level, so onStatus would be a better fit here:

Mono<ClientHttpResponse> clientResponse = client.post().uri("/resource")     .retrieve()     .onStatus(httpStatus -> HttpStatus.CREATED.equals(httpStatus),          response -> response.bodyToMono(String.class).map(body -> new MyException(body)))     bodyToXYZ(...); 

Or

Mono<ResponseEntity<String>> result = client.post().uri("/resource")     .exchange()     .flatMap(response -> response.toEntity(String.class))     .flatMap(entity -> {         // return Mono.just(entity) or Mono.error() depending on the response      }); 

Note that getting the whole response body might not be a good idea if you expect large response bodies; in that case, you'd be buffering in memory large amounts of data.

like image 28
Brian Clozel Avatar answered Sep 30 '22 02:09

Brian Clozel