如何从Spring 5 WebClient ClientResponse中提取响应标头和状态代码



我是Spring Reactive框架的新手,并试图将Springboot 1.5.x代码转换为Springboot 2.0。我需要在Spring 5 WebClient ClientResponse的一些过滤,正文和状态代码之后返回响应标头。我不想使用 block(( 方法,因为它会将其转换为同步调用。 我能够使用bodyToMono非常轻松地获得响应体。此外,如果我只是返回ClientResponse,我会得到状态代码,标头和正文,但我需要根据statusCode和标头参数处理响应。 我尝试订阅,平面地图等,但没有任何效果。

例如 - 下面的代码将返回响应正文

Mono<String> responseBody =  response.flatMap(resp -> resp.bodyToMono(String.class));

但是类似的范式无法获取statusCode和Response标头。 有人可以帮助我使用 Spring 5 反应式框架提取状态代码和标头参数。

您可以使用 webclient 的交换功能,例如

Mono<String> reponse = webclient.get()
.uri("https://stackoverflow.com")
.exchange()
.doOnSuccess(clientResponse -> System.out.println("clientResponse.headers() = " + clientResponse.headers()))
.doOnSuccess(clientResponse -> System.out.println("clientResponse.statusCode() = " + clientResponse.statusCode()))
.flatMap(clientResponse -> clientResponse.bodyToMono(String.class));

然后你可以转换身体到单声道等

在 SpringBoot 2.4.x/Spring 5.3 之后,WebClientexchange方法被弃用,取而代之的是retrieve,因此您必须使用 ResponseEntity 获取标头和响应状态,如以下示例所示:

webClient
.method(HttpMethod.POST)
.uri(uriBuilder -> uriBuilder.path(loginUrl).build())
.bodyValue(new LoginBO(user, passwd))
.retrieve()
.toEntity(LoginResponse.class)
.filter(
entity ->
entity.getStatusCode().is2xxSuccessful()
&& entity.getBody() != null
&& entity.getBody().isLogin())
.flatMap(entity -> Mono.justOrEmpty(entity.getHeaders().getFirst(tokenHeader)));

我还需要检查响应详细信息(标头,状态等(和正文。

我能够做到这一点的唯一方法是使用带有两个subscribe().exchange(),如以下示例所示:

Mono<ClientResponse> clientResponse = WebClient.builder().build()
.get().uri("https://stackoverflow.com")
.exchange();
clientResponse.subscribe((response) -> {
// here you can access headers and status code
Headers headers = response.headers();
HttpStatus stausCode = response.statusCode();
Mono<String> bodyToMono = response.bodyToMono(String.class);
// the second subscribe to access the body
bodyToMono.subscribe((body) -> {
// here you can access the body
System.out.println("body:" + body);
// and you can also access headers and status code if you need
System.out.println("headers:" + headers.asHttpHeaders());
System.out.println("stausCode:" + stausCode);
}, (ex) -> {
// handle error
});
}, (ex) -> {
// handle network error
});

我希望它有所帮助。 如果有人知道更好的方法,请告诉我们。

如上所述,交换已被弃用,因此我们使用 retrieve((。这就是我在发出请求后返回代码的方式。

public HttpStatus getResult() {
WebClient.ResponseSpec response = client
.get()
.uri("/hello")
.accept(MediaType.APPLICATION_JSON)
.retrieve();
return Optional.of(response.toBodilessEntity().block().getStatusCode()).get();
}

根据评论的另一种选择,我最近尝试过。这通常建议用于异步调用,但我们可以将它用于两者。

MyClass responseMono = this.webClient
.get()
.uri("myapipath"}")
.retrieve()
.bodyToMono(MyClass.class)
.block();
return responseMono;
httpClient
.get()
.uri(url)
.retrieve()
.toBodilessEntity()
.map(reponse -> Tuple2(reponse.statusCode, reponse.headers))

对于状态代码,您可以尝试以下操作:

Mono<HttpStatus> status = webClient.get()
.uri("/example")
.exchange()
.map(response -> response.statusCode());

对于标头:

Mono<HttpHeaders> result = webClient.get()
.uri("/example")
.exchange()
.map(response -> response.headers().asHttpHeaders());

如果您使用的是WebClient,则可以配置 Spring boot>= 2.1.0 以记录请求和响应:

spring.http.log-request-details: true
logging.level.org.springframework.web.reactive.function.client.ExchangeFunctions: TRACE

如冲刺启动文档中所述,如果您也希望记录标头,则必须添加

Consumer<ClientCodecConfigurer> consumer = configurer ->
configurer.defaultCodecs().enableLoggingRequestDetails(true);
WebClient webClient = WebClient.builder()
.exchangeStrategies(ExchangeStrategies.builder().codecs(consumer).build())
.build();

但请注意,这可以记录敏感信息。

您可以使用flatMapMono中提取对象

将您的请求作为 MAP 发送 以地图形式获取您的响应 检查过滤器功能中的所有状态代码

private static Mono<ClientResponse>  filterFunc(ClientResponse response) {
HttpStatus status = response.statusCode();
if (HttpStatus.BAD_REQUEST.equals(status)) {
return response.bodyToMono(String.class)
.flatMap(body -> Mono.error(new MyCustomHttpErrorException(  HttpStatus.BAD_REQUEST ,  body) ));
} else if (!HttpStatus.OK.equals(status)) {
return response.bodyToMono(String.class)
.flatMap(body -> Mono.error(  new MyCustomHttpErrorException(  status ,  body) )); 
}
return Mono.just(response);
}
this.webClient = builder.baseUrl(serverUrl)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.filter(ExchangeFilterFunction.ofResponseProcessor(MyClass::exchangeFilterResponseProcessor))  
.build();

Mono<Map<String, String>> mnoObjStream;
mnoObjStream = webClient.post().uri("/abc")
.body(BodyInserters
.fromFormData("key1","val1").with("key2","val2").with("key3","val3"))
.retrieve()  // Note use retrieve  because  exchange() has been deprecated.
.bodyToMono(new ParameterizedTypeReference<Map<String, String>>() {});  
resp = mnoObjStream.block();

exchangeToMono 除了一个函数作为 responseHandler。如果我们创建一个实现函数的类,我们可以将标头、状态存储在类变量中,并让该方法返回主体。

MyClientResponsehandler handler = new ()..
String bodyAsString = webClient.get().exchangeToMono(handler).block();
class MyClientResponseHandler implements Function<ClientResponse, Mono<String>>{
private List<String> contentDispositions;
@Override
public Mono<String> apply(ClientResponse cr){
contentsDisposition = cr.headers.header("CONTENTDISPO ...");
return Mono.just(cr).flatMap(cr1->cr1.bodyToMono(String.class));
}
} 

这样,可以在 block(( 之后从 API 调用中检索主体。标头,状态可以存储在处理程序中以进行后处理

获取标头

HttpHeaders result = webClient.get()
.uri("/example")
.exchange()
.map(response -> response.headers().asHttpHeaders()).block();

最新更新