当前位置: 首页 > 知识库问答 >
问题:

如何注销对Spring WebFlux WebClient请求的失败响应的主体,同时将响应返回给调用方?

乔俊才
2023-03-14

我对反应编程非常陌生,我有一个REST服务,它接受一个请求,然后使用WebFlux WebClient调用另一个API。当API以4xx或5xx响应进行响应时,我希望将响应体记录在我的服务中,然后将响应传递给调用方。我已经找到了许多处理响应日志记录的方法,但它们通常会将mono.error返回给调用方,这不是我想要做的。我几乎可以做到这一点,但是当我向我的服务发出请求时,当我返回API返回的4xx代码时,我的客户机只是挂起等待响应的正文,而服务似乎从未完成对流的处理。我使用的是Spring Boot版本2.2.4.发行版。

我得到的是:

@PostMapping(path = "create-order")
public Mono<ResponseEntity<OrderResponse>> createOrder(@Valid @RequestBody CreateOrderRequest createOrderRequest) {
    return orderService.createOrder(createOrderRequest);
}
public Mono<ResponseEntity<OrderResponse>> createOrder(CreateOrderRequest createOrderRequest) {
    return this.webClient
            .mutate()
            .filter(OrderService.errorHandlingFilter(ORDERS_URI, createOrderRequest))
            .build()
            .post()
            .uri(ORDERS_URI)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(createOrderRequest)
            .exchange()
            .flatMap(response -> response.toEntity(OrderResponse.class));
}

public static ExchangeFilterFunction errorHandlingFilter(String uri, CreateOrderRequest request) {
    return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
        if (clientResponse.statusCode() != null && (clientResponse.statusCode().is5xxServerError() || clientResponse.statusCode().is4xxClientError())) {
            return clientResponse.bodyToMono(String.class)
                    .flatMap(errorBody -> OrderService.logResponseError(clientResponse, uri, request, errorBody));
        } else {
            return Mono.just(clientResponse);
        }
    });
}

static Mono<ClientResponse> logResponseError(ClientResponse response, String attemptedUri, CreateOrderRequest orderRequest, String responseBody) {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    try {
        log.error("Response code {} received when attempting to hit {}, request:{}, response:{}",
                response.rawStatusCode(), attemptedUri, objectMapper.writeValueAsString(orderRequest),
                responseBody);
    } catch (JsonProcessingException e) {
        log.error("Error attempting to serialize request object when reporting on error for request to {}, with code:{} and response:{}",
                attemptedUri, response.rawStatusCode(), responseBody);
    }
    return Mono.just(response);
}
return Mono.error(new Exception("Some error"));

我在客户端收到一个500,请求就完成了。如果有人知道为什么当我尝试发回响应时它不会完成,我很想知道我做错了什么。

共有1个答案

梁丘兴腾
2023-03-14

鱼与熊掌不可兼得!

这里的问题是,您正在尝试消耗响应的主体两次,这是不允许的。通常这样做会得到一个错误。

一旦进入

return clientResponse.bodyToMono(String.class) 
response.toEntity(OrderResponse.class)
@Override
public <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyType) {
    return WebClientUtils.toEntity(this, bodyToMono(bodyType));
}
public Mono<ResponseEntity<OrderResponse>> createOrder(CreateOrderRequest createOrderRequest) {
    return this.webClient
            //no need for mutate unless you already have things specified in 
            //base webclient?
            .post()
            .uri(ORDERS_URI)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(createOrderRequest)
            .exchange()
            //Here you map the response to an entity first
            .flatMap(response -> response.toEntity(OrderResponse.class))
            //Then run the errorHandler to do whatever
            //Use doOnNext since there isn't any reason to return anything
            .doOnNext(response -> 
                errorHandler(ORDERS_URI,createOrderRequest,response));

}

//Void doesn't need to return
public static void  errorHandler(String uri, CreateOrderRequest request,ResponseEntity<?> response) {
    if( response.getStatusCode().is5xxServerError() 
        || response.getStatusCode().is4xxClientError())
            //run log method if 500 or 400
            OrderService.logResponseError(response, uri, request);
}

//No need for redundant final param as already in response
static void logResponseError(ResponseEntity<?> response, String attemptedUri, CreateOrderRequest orderRequest) {
    //Do the log stuff
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    try {
        log.error("Response code {} received when attempting to hit {}, request:{}, response:{}",
                response.getStatusCodeValue(), attemptedUri, objectMapper.writeValueAsString(orderRequest),
                response.getBody());
    } catch (JsonProcessingException e) {
        log.error("Error attempting to serialize request object when reporting on error for request to {}, with code:{} and response:{}",
                attemptedUri, response.getStatusCodeValue(), response.getBody());
    }
}

请注意,没有使用ExchangeFilter的真正理由,因为您实际上并没有执行任何筛选,只是根据响应执行一个操作

 类似资料:
  • 我打下面的url使用任何Rest客户端,我得到api响应:400坏请求与响应体 输入参数 但是java simple client没有显示响应主体。。下面是java代码。。它只给出了400个坏请求。

  • 我在Alamofire 5.3中收到了大量失败的请求,其中响应对象本身为nil,或者错误为“无法解析响应”。我可以从服务器日志中看到,所有这些请求都返回有效。 这是我的设置: API管理器类: AccessTokenInterceptor: 这个拦截器从插入我的授权令牌 我也使用标准路由器的URL刚需转换,编码是通过JSON序列化(字典)或可编码协议(对象) 奇怪的是,我不认为我在做任何不同于我使

  • 我正在使用带有Robospice(1.4.14)的Revovit(1.6.1)从一些服务中获取数据(响应应该在JSON中)。 在某些情况下,我可能会收到一个HTML错误页面,而不是一个JSON响应。服务器返回一个200状态代码,我不能改变它。在这种情况下,RoboSpice将调用方法。 在那里,我可以获得原始的expetion,但正文是。我是这样得到的: 在研究了reverfit的源代码之后,我发

  • 使用Guzzle,我正在使用JSON格式的一些外部API,通常我获取数据 $data = $request- 但是我无法从这个不同的api中获取数据。数据似乎没有出现在“响应体”中。 这个api调用有效:https://i.ibb.co/80Yk6dx/Screenshot-2.png 这不起作用:https://i.ibb.co/C239ghy/Screenshot-3.png

  • 我在雄猫上部署了一个泽西岛 2.5.1 应用程序。我在我的网络中添加了以下配置.xml: 它打印请求和响应头。但是,它没有在响应中打印请求/响应实体/主体。 我试着和不同的情妇玩,但没有运气,比如: 感谢您的回答。看起来我在上面粘贴了错误的配置。这是我在我的网络上有什么.xml: 它记录标题。但它不记录实体。这是我在日志中看到的: 我的Jersey应用程序部署在Tomcat 7_50服务器上。 如

  • 我使用的是Spring版本4(Spring data),我想将Object作为JSON返回,我想知道以下代码即使不使用xmlRootElement注释用户类也能工作: 任何机构都可以解释吗?当我需要注释要返回为JSON的对象类时,响应体/响应实体是否自己完成工作?