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

使用Spring WebClient在错误情况下获取响应主体

邢寒
2023-03-14

我试图用一个网络客户端来替换现有的客户机代码。因此,大多数调用都需要阻塞,这样应用程序的主要部分就不需要更改。当谈到错误处理时,这会带来一些问题。必须涵盖以下几种情况:

  • 在成功的情况下,响应包含A类型的JSON对象
  • 在错误情况下(HTTP状态4xx或5xx)响应可能包含B类型的JSON对象
  • 在某些具有响应状态404的请求上,我需要返回一个与成功响应类型匹配的空List

为了产生正确的错误(异常),需要考虑错误响应。到目前为止,我还无法掌握错误主体。

我正在使用这个RestController方法来产生错误响应:

@GetMapping("/error/404")
@ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseEntity error404() {
    return ResponseEntity
            .status(HttpStatus.NOT_FOUND)
            .body(new ErrorResponse());
}

使用此响应对象:

public class ErrorResponse {    
    private String message = "Error message";    
    public String getMessage() {
        return message;
    }
}

网络客户端定义如下:

WebClient.builder()
    .baseUrl("http://localhost:8081")
    .clientConnector(connector)
    .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
    .build();

连接器类型为CloseableHttpAsyncClient(Apache Http客户端5)。

从我的测试应用程序中,我这样调用:

public String get(int httpStatus) {
    try {
        return webClient.get()
                .uri("/error/" + httpStatus)
                .retrieve()
                .onStatus(HttpStatus::isError, clientResponse -> {
                    clientResponse.bodyToMono(String.class).flatMap(responseBody -> {
                        log.error("Body from within flatMap within onStatus: {}", responseBody);
                        return Mono.just(responseBody);
                    });
                    return Mono.error(new RuntimeException("Resolved!"));
                })
                .bodyToMono(String.class)
                .flatMap(clientResponse -> {
                    log.warn("Body from within flatMap: {}", clientResponse);
                    return Mono.just(clientResponse);
                })
                .block();
    } catch (Exception ex) {
        log.error("Caught Error: ", ex);
        return ex.getMessage();
    }
}

我得到的是来自onstatus返回的RuntimeException,当然还有最终捕获的异常。我缺少来自onstatus内的body ToMono的处理。我的怀疑是,由于阻塞性质,这没有执行,因为响应体是在onstatus之后处理body ToMono的。在注释掉onstatus时,我希望我们将警告记录在平图中,这也不会发生。

最后,我想将错误处理定义为一个过滤器,这样代码就不需要在每次调用时重复,但我需要获得错误响应主体,这样可以用正确的数据填充异常。

如何在同步WebClient调用中检索错误响应?

这个问题类似于Spring Webflux:Webclient:Get body on error,它没有公认的答案,并且一些建议的方法使用的方法没有被弃用。


共有1个答案

邹昊
2023-03-14

以下是一种处理错误响应的方法:

  • 使用onStatus捕获错误http状态
webClient.get()
        .uri("/error/" + httpStatus)
        .retrieve()
        .onStatus(HttpStatus::isError, clientResponse ->
                clientResponse.bodyToMono(ErrorResponse.class)
                        .flatMap(error ->
                                Mono.error(new RuntimeException(error.getMessage()))
                        )
        )
        .bodyToMono(Response.class)

你真的不需要试试catch。如果block,如果是非错误响应,上述代码将返回Response,如果是错误响应,则抛出带有自定义消息的异常。

更新

下面是使用WireMock的完整测试

class WebClientErrorHandlingTest {
    private WireMockServer wireMockServer;

    @BeforeEach
    void init() {
        wireMockServer = new WireMockServer(wireMockConfig().dynamicPort());
        wireMockServer.start();
        WireMock.configureFor(wireMockServer.port());
    }

    @Test
    void test() {
        stubFor(post("/test")
                .willReturn(aResponse()
                        .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .withStatus(400)
                        .withBody("{\"message\":\"Request error\",\"errorCode\":\"10000\"}")
                )
        );

        WebClient webClient = WebClient.create("http://localhost:" + wireMockServer.port());

        Mono<Response> request = webClient.post()
                .uri("/test")
                .retrieve()
                .onStatus(HttpStatus::isError, clientResponse ->
                        clientResponse.bodyToMono(ErrorResponse.class)
                                .flatMap(error ->
                                        Mono.error(new RuntimeException(error.getMessage() + ": " + error.getErrorCode()))
                                )
                )
                .bodyToMono(Response.class);


        RuntimeException ex = assertThrows(RuntimeException.class, () -> request.block());
        assertEquals("Request error: 10000", ex.getMessage());
    }

    @Data
    private static class ErrorResponse {
        private String message;
        private int errorCode;
    }

    @Data
    private static class Response {
        private String result;
    }
}
 类似资料:
  • 我正在使用Spring Data Elasticsearch来获取一些聚合,并尝试使用ReactiveElasticsearchClient。我从Elasticsearch得到一个500错误,但我不知道如何获得响应的主体,以便调试请求的错误。以下是我目前掌握的情况: 为组织启用跟踪日志记录。springframework。数据elasticsearch。客户WIRE,将记录请求正文,但响应的日志行

  • 首先,如果这是一个很长的代码段,我很抱歉,但是,我想做一个模态窗口,它将你在我的用户表单中写的东西写下来,并要求你确认它。我目前正在学习Javascript,我不允许使用innerHTML,我必须动态地编写“名字”等(名字的文本),不允许只在弹出窗口内写它。我已经让大多数东西工作,但“名字”“名字”等显示为“未定义”,或者(正如你可以看到的,我在这种情况下只用名字尝试的事情)显示为“空”。 希望有

  • 我想将我的异常从我的“数据库”REST API重新抛出到我的“后端”REST API,但我丢失了原始异常的消息。 这是我通过Postman从我的“数据库”REST API中得到的: 这部分没问题。 这是我通过Postman从我的“后端”REST API得到的: 如您所见,原来的“message”字段丢失了。 我使用: Spring boot 2.2.5.发布 spring-boot-starter

  • 在我的响应中,有一个,没有任何。那么,我如何获取该响应呢? 这是我的JSON代码-

  • 问题内容: 我正在发出HTTP请求并侦听“数据”: 问题在于响应是分块的,因此“数据”只是发送回的一部分内容。 我如何将全身退回? 问题答案:

  • 我有一个rest web服务。如果抛出任何异常,web服务将返回http 500错误。但我不想发送这个带有异常堆栈跟踪的错误响应。我只想发送错误代码和错误消息。我没有做到这一点。我怎么能这么做? 我已经尝试过@ControllerAdvice和@ExceptionHandler注释,但我做不到。当我使用@ResponseStatus注释时,总是发送静态“reason”值。如何设置此值?谢谢你的帮助