Java 在 WebFlux WebClient 中测试状态代码时如何获取响应正文?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/46759603/
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
How to get response body when testing the status code in WebFlux WebClient?
提问by CoryO
How do you retrieve the response body when trying to throw an exception based on the returned status code? For instance, lets say I want to throw an exception and reject HTTP 201.
尝试根据返回的状态代码抛出异常时如何检索响应正文?例如,假设我想抛出异常并拒绝 HTTP 201。
client.post().exchange().doOnSuccess(response -> {
if (response.statusCode().value() == 201) {
throw new RuntimeException();
}
}
How can I populate the exception with the response's body so I can throw a detailed WebClientResponseException
?
如何使用响应正文填充异常,以便抛出详细信息WebClientResponseException
?
Should I be using a different method to test the response status code?
我应该使用不同的方法来测试响应状态代码吗?
edit:I am trying to duplicate the following functionality while using exchange()
instead.
编辑:我试图在使用时复制以下功能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
);
});
}
回答by Brian Clozel
doOn**
operators are side-effects operators and should be used for logging purposes, for example.
doOn**
运算符是副作用运算符,应该用于记录目的,例如。
Here, you'd like to implement that behavior at the pipeline level, so onStatus
would be a better fit here:
在这里,您想在管道级别实现该行为,因此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(...);
Or
或者
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
});
Note that getting the whole response body might not be a good idea if you expect large response bodies; in that case, you'd be buffering in memory large amounts of data.
请注意,如果您期望大型响应主体,则获取整个响应主体可能不是一个好主意;在这种情况下,您将在内存中缓冲大量数据。
回答by CoryO
A bit of trial and error got me the following that appears to do the trick.
一些反复试验让我得到了以下似乎可以解决问题的方法。
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();
回答by ROCKY
You could achieve like this by having a custom ExchangeFilterFunction and then hooking this up with WebClient.Builder before you build WebClient.
您可以通过自定义 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);
}
});
}
You can use the above like this:
你可以像这样使用上面的:
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) );
So any 4XX or 5XX HttpResponse will actually throw CustomWebClientResponseException and you can configure some global exception handler and do what you like to with this. Atleast using ExchangeFilterFunction you can have global place to handle things like this or add custom headers and stuff too.
因此,任何 4XX 或 5XX HttpResponse 实际上都会抛出 CustomWebClientResponseException 并且您可以配置一些全局异常处理程序并使用它做您喜欢的事情。至少使用 ExchangeFilterFunction 您可以拥有全局位置来处理这样的事情或添加自定义标题和内容。