reactive-programming - 如何在响应式(Reactive) Spring WebClient 调用的错误部分抛出异常?

标签 reactive-programming project-reactor spring-webclient spring-reactive spring-reactor

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

@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。不幸的是,测试失败了,并且 Exception 被包装到了 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 部分才能使我的方法真正抛出异常?

最佳答案

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

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

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

您还可以使用.block()此处(测试是极少数情况之一,这不太可能出现问题)。

但如果我是你,我会养成使用 StepVerifier 的习惯来自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

}

关于reactive-programming - 如何在响应式(Reactive) Spring WebClient 调用的错误部分抛出异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58198961/

相关文章:

javascript - 如何使用 RxJS 恢复 websocket 连接

kotlin - 如何使用组合函数压缩超过 8 个单声道

java - 使用Spring WebClient在java中上传文件

java - Spring WebClient - 如何处理错误场景

android - Android 应用程序 Meteor 后端的 OpeningHandshakeException

database - Slick 3.0 在数据库驱动程序级别是响应式(Reactive)/异步的吗?适用于哪些数据库?

javascript - 在 R Shiny 中触发 react 流的键盘快捷键?

java - 从 3 个不同的单声道创建实体

spring-boot - 响应式(Reactive) Elasticsearch 和分页

java - react 堆项目 : ConnectableFlux auto-connecting on demand