Java Spring Webflux:Webclient:出错时获取正文
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/44593066/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Webflux : Webclient : Get body on error
提问by adrien le roy
I am using the webclient from spring webflux, like this :
我正在使用 spring webflux 的 webclient,如下所示:
WebClient.create()
.post()
.uri(url)
.syncBody(body)
.accept(MediaType.APPLICATION_JSON)
.headers(headers)
.exchange()
.flatMap(clientResponse -> clientResponse.bodyToMono(tClass));
It is working well. I now want to handle the error from the webservice I am calling (Ex 500 internal error). Normally i would add an doOnError on the "stream" and isu the Throwable to test the status code,
它运行良好。我现在想处理来自我正在调用的网络服务的错误(Ex 500 内部错误)。通常我会在“流”上添加一个 doOnError 并使用 Throwable 来测试状态代码,
But my issue is that I want to get the body provided by the webservice because it is providing me a message that i would like to use.
但我的问题是我想获得网络服务提供的正文,因为它为我提供了一条我想使用的消息。
I am looking to do the flatMap whatever happen and test myself the status code to deserialize or not the body.
无论发生什么,我都希望做 flatMap 并测试自己的状态代码以反序列化或不反序列化主体。
采纳答案by Arjen Poutsma
Note that as of writing this, 5xx errors no longer result in an exception from the underlying Netty layer. See https://github.com/spring-projects/spring-framework/commit/b0ab84657b712aac59951420f4e9d696c3d84ba2
请注意,在撰写本文时,5xx 错误不再导致底层 Netty 层出现异常。见https://github.com/spring-projects/spring-framework/commit/b0ab84657b712aac59951420f4e9d696c3d84ba2
回答by Artem Bilan
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));
回答by adrien le roy
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.
我们终于明白发生了什么:默认情况下,Netty 的 httpclient (HttpClientRequest) 被配置为在服务器错误(响应 5XX)而不是客户端错误(4XX)时失败,这就是它总是发出异常的原因。
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 :
我们所做的是扩展 AbstractClientHttpRequest 和 ClientHttpConnector 来配置 httpclient 的行为方式,当我们调用 WebClient 时,我们使用我们自定义的 ClientHttpConnector :
WebClient.builder().clientConnector(new CommonsReactorClientHttpConnector()).build();
回答by Mohale
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
阅读这篇文章以获取更多示例链接,当我遇到类似的错误处理问题时,我发现它很有帮助
回答by Alan Ho
I had just faced the similar situation and I found out webClient does not throw any exception even it is getting 4xx/5xx responses. In my case, I use webclient to first make a call to get the response and if it is returning 2xx response then I extract the data from the response and use it for making the second call. If the first call is getting non-2xx response then throw an exception. Because it is not throwing exception so when the first call failed and the second is still be carried on. So what I did is
我刚刚遇到了类似的情况,我发现即使 webClient 收到 4xx/5xx 响应也不会抛出任何异常。就我而言,我使用 webclient 首先调用以获取响应,如果它返回 2xx 响应,则我从响应中提取数据并将其用于进行第二次调用。如果第一次调用得到非 2xx 响应,则抛出异常。因为它不会抛出异常,所以当第一次调用失败而第二次调用时仍然会继续。所以我所做的是
return webClient.post().uri("URI")
.header(HttpHeaders.CONTENT_TYPE, "XXXX")
.header(HttpHeaders.ACCEPT, "XXXX")
.header(HttpHeaders.AUTHORIZATION, "XXXX")
.body(BodyInserters.fromObject(BODY))
.exchange()
.doOnSuccess(response -> {
HttpStatus statusCode = response.statusCode();
if (statusCode.is4xxClientError()) {
throw new Exception(statusCode.toString());
}
if (statusCode.is5xxServerError()) {
throw new Exception(statusCode.toString());
}
)
.flatMap(response -> response.bodyToMono(ANY.class))
.map(response -> response.getSomething())
.flatMap(something -> callsSecondEndpoint(something));
}
回答by Omar ZRIDI
I prefer to use the methods provided by the ClientResponse to handle http errors and throw exceptions:
我更喜欢使用 ClientResponse 提供的方法来处理 http 错误和抛出异常:
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().
实际上,它与 DefaultWebClient 的 DefaultResponseSpec 中用于处理错误的逻辑相同。DefaultResponseSpec 是 ResponseSpec 的一个实现,如果我们使用retrieve()而不是exchange(),我们就会拥有它。