有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java在WebFlux WebClient中测试状态代码时如何获取响应体?

当试图基于返回的状态代码引发异常时,如何检索响应正文?例如,假设我想抛出一个异常并拒绝HTTP 201

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

如何用响应的主体填充异常,以便抛出详细的WebClientResponseException

我应该使用不同的方法来测试响应状态代码吗

编辑:我试图在使用exchange()的同时复制以下功能

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

共 (3) 个答案

  1. # 1 楼答案

    doOn**操作符是副作用操作符,例如,应该用于日志记录目的

    在这里,您希望在管道级别实现该行为,因此onStatus更适合这里:

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

    或者

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

    请注意,如果你希望得到大的响应体,那么获取整个响应体可能不是一个好主意;在这种情况下,您将在内存中缓冲大量数据

  2. # 2 楼答案

    经过一点尝试和错误,我得到了以下似乎能达到目的的东西

    Mono<ClientResponse> mono = client.get().exchange()
            .flatMap(response -> {
                if (HttpStatus.CREATED.equals(response.statusCode())) {
                    return Mono.just(response);
                } else {
                    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])
                            .flatMap(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 Mono.error(new WebClientResponseException(msg,
                                        response.statusCode().value(),
                                        response.statusCode().getReasonPhrase(),
                                        response.headers().asHttpHeaders(),
                                        bodyBytes,
                                        charset
                                        ));
                            });
                }
            })
            .retry(3);
    final CompletableFuture<ClientResponse> future = mono.toFuture();
    
  3. # 3 楼答案

    您可以通过使用一个定制的ExchangeFilter函数,然后将其与WebClient连接起来来实现这一点。在构建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);
                }
            });
        }
    

    您可以这样使用上面的选项:

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

    因此,任何4XX或5XX HttpResponse实际上都会抛出CustomWebClientResponseException,您可以配置一些全局异常处理程序,并使用它执行您喜欢的操作。至少使用ExchangeFilterFunction,您可以在全局位置处理类似的事情,或者添加自定义标题和其他内容