1

如果发生错误,我希望以下方法引发自定义异常:

@Service
public class MyClass {

    private final WebClient webClient;

    public MatcherClient(@Value("${my.url}") final String myUrl) {
        this.webClient = WebClient.create(myUrl);
    }

    public void sendAsync(String request) {

        Mono<MyCustomResponse> result = webClient.post()
            .header(HttpHeaders.CONTENT_TYPE, "application/json")
            .body(BodyInserters.fromObject(request))
            .retrieve()
            .doOnError(throwable -> throw new CustomException(throwable.getMessage()))
            .subscribe(response -> log.info(response));

    }

}

我还设置了一个单元测试,期望抛出 CustomException。不幸的是,测试失败了,异常被包裹在一个 Mono 对象中。这里还有测试代码供参考:

@Test(expected = CustomException.class)
public void testSendAsyncRethrowingException() {
    MockResponse mockResponse = new MockResponse()
        .setHeader(HttpHeaders.CONTENT_TYPE, "application/json")
        .setResponseCode(500).setBody("Server error");
    mockWebServer.enqueue(mockResponse);

    matcherService.matchAsync(track);
}

我正在使用MockWebServer来模拟测试中的错误。

那么,如果调用,我应该如何实现 doOnError 或 onError 部分,以使我的方法真正抛出异常?

4

3 回答 3

2

我建议公开一个Mono<Void>从 web 客户端返回的反应式 API,特别是如果您将方法命名为“sendAsync”。如果您必须阻止调用返回/失败,则它不是异步的。如果您想提供sendSync()替代方案,您可以随时调用sendAsync().block()

对于异常的转换,可以使用专用onErrorMap运算符。

对于测试,问题是,您不能 100% 使用纯命令式和同步构造(如 JUnit 的Test(expected=?)注释)测试异步代码。(尽管一些反应式运算符不会引起并行性,因此这种测试有时可以工作)。

您也可以.block()在此处使用(测试是不太可能出现问题的罕见情况之一)。

但如果我是你,我会养成使用StepVerifierfrom的习惯reactor-test。举一个总结我的建议的例子:

@Service
public class MyClass {

    private final WebClient webClient;

    public MatcherClient(@Value("${my.url}") final String myUrl) {
        this.webClient = WebClient.create(myUrl);
    }

    public Mono<MyCustomResponse> sendAsync(String request) {
        return webClient.post()
            .header(HttpHeaders.CONTENT_TYPE, "application/json")
            .body(BodyInserters.fromObject(request))
            .retrieve()
            .onErrorMap(throwable -> new CustomException(throwable.getMessage()))
            //if you really need to hardcode that logging
            //(can also be done by users who decide to subscribe or further add operators)
            .doOnNext(response -> log.info(response));
    }
}

和测试:

@Test(expected = CustomException.class)
public void testSendAsyncRethrowingException() {
    MockResponse mockResponse = new MockResponse()
        .setHeader(HttpHeaders.CONTENT_TYPE, "application/json")
        .setResponseCode(500).setBody("Server error");
    mockWebServer.enqueue(mockResponse);

    //Monos are generally lazy, so the code below doesn't trigger any HTTP request yet
    Mono<MyCustomResponse> underTest = matcherService.matchAsync(track);

    StepVerifier.create(underTest)
    .expectErrorSatisfies(t -> assertThat(t).isInstanceOf(CustomException.class)
        .hasMessage(throwable.getMessage())
    )
    .verify(); //this triggers the Mono, compares the
               //signals to the expectations/assertions and wait for mono's completion

}
于 2019-10-03T12:46:35.680 回答
1

每当收到状态码为 4xx 或 5xx 的响应时,WebClient 中的 retrieve() 方法就会引发 WebClientResponseException。

1.可以使用onStatus()方法自定义异常

public Mono<JSONObject> listGithubRepositories() {
 return webClient.get()
        .uri(URL)
        .retrieve()
        .onStatus(HttpStatus::is4xxClientError, clientResponse ->
            Mono.error(new MyCustomClientException())
        )
        .onStatus(HttpStatus::is5xxServerError, clientResponse ->
            Mono.error(new MyCustomServerException())
        )
        .bodyToMono(JSONObject.class);
}

2.通过检查响应状态抛出自定义异常

   Mono<Object> result = webClient.get().uri(URL).exchange().log().flatMap(entity -> {
        HttpStatus statusCode = entity.statusCode();
        if (statusCode.is4xxClientError() || statusCode.is5xxServerError())
        {
            return Mono.error(new Exception(statusCode.toString()));
        }
        return Mono.just(entity);
    }).flatMap(clientResponse -> clientResponse.bodyToMono(JSONObject.class))

参考: https ://www.callicoder.com/spring-5-reactive-webclient-webtestclient-examples/

于 2021-05-05T12:27:51.493 回答
-1

Instead of using doOnError I swiched to subscribe method accepting also an error consumer:

Mono<MyCustomResponse> result = webClient.post()
            .header(HttpHeaders.CONTENT_TYPE, "application/json")
            .body(BodyInserters.fromObject(request))
            .retrieve()
            .subscribe(response -> log.info(response),
                       throwable -> throw new CustomException(throwable.getMessage()));

This documentation helps a lot: https://projectreactor.io/docs/core/release/reference/index.html#_error_handling_operators

于 2019-10-02T11:46:29.063 回答