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

时间:2017-10-15 20:20:29

标签: java spring-webflux

在尝试根据返回的状态代码抛出异常时,如何检索响应主体?例如,假设我想抛出异常并拒绝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 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:4)

您可以通过使用自定义的ExchangeFilterFunction,然后在构建WebClient之前使用WebClient.Builder将其挂起来实现这一目标。

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,您可以配置一些全局异常处理程序并使用它执行您喜欢的操作。 Atleast使用ExchangeFilterFunction你可以拥有全局的地方来处理这样的事情或添加自定义标题和东西。

答案 2 :(得分:-1)

一些试验和错误让我得到了以下似乎可以解决问题。

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