在错误情况下从 WebClient 获取响应正文的正确方法是啥?
Posted
技术标签:
【中文标题】在错误情况下从 WebClient 获取响应正文的正确方法是啥?【英文标题】:What's the correct way to get the response body from a WebClient in an error case?在错误情况下从 WebClient 获取响应正文的正确方法是什么? 【发布时间】:2019-11-09 13:48:36 【问题描述】:我是 WebClient
和响应式编程的新手。我想从请求中获取响应正文。如果发生错误,必须记录 http 代码、标头和正文,但仍应返回正文。
经过大量挖掘和谷歌搜索,我找到了两种解决方案。但在我看来,两者都过于复杂。有没有更简单的解决方案?
留在Mono
我找到了这个解决方案:
public Mono<String> log(ProtocolLine protocolLine)
return webClient.post()
.uri("/log")
.body(BodyInserters.fromObject(protocolLine))
.exchange()
.flatMap(clientResponse ->
Mono<String> stringMono = clientResponse.bodyToMono(String.class);
CompletableFuture<String> stringCompleteFuture = new CompletableFuture<String>();
Mono<String> bodyCompletedMono = Mono.fromFuture(stringCompleteFuture);
if (clientResponse.statusCode().isError())
stringMono.subscribe(bodyString ->
LOGGER.error("HttpStatusCode = ", clientResponse.statusCode());
LOGGER.error("HttpHeaders = ", clientResponse.headers().asHttpHeaders());
LOGGER.error("ResponseBody = ", bodyString);
stringCompleteFuture.complete(bodyString);
);
return bodyCompletedMono;
);
基于Flux
,它需要更少的代码。但如果我知道只会有一个结果,我想我不应该使用 Flux。
public Flux<String> log(ProtocolLine protocolLine)
return webClient.post()
.uri("/log")
.body(BodyInserters.fromObject(protocolLine))
.exchange()
.flux()
.flatMap(clientResponse ->
Flux<String> stringFlux = clientResponse.bodyToFlux(String.class).share();
if (clientResponse.statusCode().isError())
stringFlux.subscribe(bodyString ->
LOGGER.error("HttpStatusCode = ", clientResponse.statusCode());
LOGGER.error("HttpHeaders = ", clientResponse.headers().asHttpHeaders());
LOGGER.error("ResponseBody = ", bodyString);
);
return stringFlux;
);
【问题讨论】:
【参考方案1】:这两种解决方案都是丑陋和错误的。您几乎不应该在反应式管道的中间订阅。订阅者通常是调用客户端,而不是您自己的应用程序。
public Mono<String> log(ProtocolLine protocolLine)
return webClient.post()
.uri("/log")
.body(BodyInserters.fromObject(protocolLine))
.exchange()
.flatMap(clientResponse -> clientResponse.bodyToMono(String.class)
.doOnSuccess(body ->
if (clientResponse.statusCode().isError())
log.error("HttpStatusCode = ", clientResponse.statusCode());
log.error("HttpHeaders = ", clientResponse.headers().asHttpHeaders());
log.error("ResponseBody = ", body);
));
在这里你可以看到思维方式。我们总是使用我们的clientResponse
并将它的主体映射到一个字符串。然后,当订阅者(我们的调用客户端)使用此 Mono
时,我们 doOnSuccess
并检查状态代码是否有错误,如果是这种情况我们记录。
doOnSuccess
方法返回 void,因此它不会“消耗”单声道或任何东西,它只是在这个 Mono
说它“本身有东西”时触发一些东西,当它“完成”时,可以说。
这可以与Flux
以相同的方式使用。
【讨论】:
感谢您的回答。如果出现错误,我可以知道如何抛出异常。 @ThomasAndolf 如果使用retrieve() 而不是exchange(),你会怎么做?并且成功响应和错误响应包含不同的类型(成功返回SuccessResponse.class,错误返回ErrorResponse.class,都序列化成json)? 您使用onStatus
方法,如官方文档所示。 docs.spring.io/spring/docs/current/spring-framework-reference/… 然后你要求的是你想从同一个函数返回 2 种不同类型的对象。您不能从同一函数返回(例如)String 或 Int。如果您阅读反应器文档,您会返回一个单声道错误(包含 say en 异常或其他内容),然后在链中稍后会出现 .doOnError
。
在 Spring 5 中,exchange() 已被弃用:是否有另一种方法可以在发生错误时获取正文?使用retrieve() 或exchangeToMono(),我就是搞不定。
ExchangeToMono(response -> ...) 给你一个响应,你可以做任何你想做的事以上是关于在错误情况下从 WebClient 获取响应正文的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
在 WebFlux WebClient 中测试状态码时如何获取响应正文?
如何记录 spring-webflux WebClient 请求 + 响应详细信息(正文、标头、elasped_time)?