Spring Webflux : Webclient : Get body on error

后端 未结 8 2232
失恋的感觉
失恋的感觉 2021-02-05 09:04

I am using the webclient from spring webflux, like this :

WebClient.create()
            .post()
            .uri(url)
            .syncBody(body)
            .a         


        
相关标签:
8条回答
  • 2021-02-05 09:23

    I prefer to use the methods provided by the ClientResponse to handle http errors and throw exceptions:

    WebClient.create()
             .post()
             .uri( url )
             .body( bodyObject == null ? null : BodyInserters.fromValue( bodyObject ) )
             .accept( MediaType.APPLICATION_JSON )
             .headers( headers )
             .exchange()
             .flatMap( clientResponse -> {
                 //Error handling
                 if ( clientResponse.statusCode().isError() ) { // or clientResponse.statusCode().value() >= 400
                     return clientResponse.createException().flatMap( Mono::error );
                 }
                 return clientResponse.bodyToMono( clazz )
             } )
             //You can do your checks: doOnError (..), onErrorReturn (..) ...
             ...
    

    In fact, it's the same logic used in the DefaultResponseSpec of DefaultWebClient to handle errors. The DefaultResponseSpec is an implementation of ResponseSpec that we would have if we made a retrieve() instead of exchange().

    0 讨论(0)
  • 2021-02-05 09:25

    I do something like this:

    Mono<ClientResponse> responseMono = requestSpec.exchange()
                .doOnNext(response -> {
                    HttpStatus httpStatus = response.statusCode();
                    if (httpStatus.is4xxClientError() || httpStatus.is5xxServerError()) {
                        throw new WebClientException(
                                "ClientResponse has erroneous status code: " + httpStatus.value() +
                                        " " + httpStatus.getReasonPhrase());
                    }
                });
    

    and then:

    responseMono.subscribe(v -> { }, ex -> processError(ex));
    
    0 讨论(0)
  • 2021-02-05 09:27

    Don't we have onStatus()?

        public Mono<Void> cancel(SomeDTO requestDto) {
            return webClient.post().uri(SOME_URL)
                    .body(fromObject(requestDto))
                    .header("API_KEY", properties.getApiKey())
                    .retrieve()
                    .onStatus(HttpStatus::isError, response -> {
                        logTraceResponse(log, response);
                        return Mono.error(new IllegalStateException(
                                String.format("Failed! %s", requestDto.getCartId())
                        ));
                    })
                    .bodyToMono(Void.class)
                    .timeout(timeout);
        }
    

    And:

        public static void logTraceResponse(Logger log, ClientResponse response) {
            if (log.isTraceEnabled()) {
                log.trace("Response status: {}", response.statusCode());
                log.trace("Response headers: {}", response.headers().asHttpHeaders());
                response.bodyToMono(String.class)
                        .publishOn(Schedulers.elastic())
                        .subscribe(body -> log.trace("Response body: {}", body));
            }
        }
    
    0 讨论(0)
  • 2021-02-05 09:29

    You could also do this

    return webClient.getWebClient()
     .post()
     .uri("/api/Card")
     .body(BodyInserters.fromObject(cardObject))
     .exchange()
     .flatMap(clientResponse -> {
         if (clientResponse.statusCode().is5xxServerError()) {
            clientResponse.body((clientHttpResponse, context) -> {
               return clientHttpResponse.getBody();
            });
         return clientResponse.bodyToMono(String.class);
       }
       else
         return clientResponse.bodyToMono(String.class);
    });
    

    Read this article for more examples link, I found it to be helpful when I experienced a similar problem with error handling

    0 讨论(0)
  • 2021-02-05 09:33

    We have finally understood what is happening : By default the Netty's httpclient (HttpClientRequest) is configured to fail on server error (response 5XX) and not on client error (4XX), this is why it was always emitting an exception.

    What we have done is extend AbstractClientHttpRequest and ClientHttpConnector to configure the httpclient behave the way the want and when we are invoking the WebClient we use our custom ClientHttpConnector :

     WebClient.builder().clientConnector(new CommonsReactorClientHttpConnector()).build();
    
    0 讨论(0)
  • 2021-02-05 09:38

    I got the error body by doing like this:

    webClient
    ...
    .retrieve()    
    .onStatus(HttpStatus::isError, response -> response.bodyToMono(String.class) // error body as String or other class
                                                       .flatMap(error -> Mono.error(new RuntimeException(error)))) // throw a functional exception
    .bodyToMono(MyResponseType.class)
    .block();
    
    0 讨论(0)
提交回复
热议问题