在另一个方法中处理来自 Spring WebClient 的错误

Handling errors from Spring WebClient in another method

在 Spring 引导应用程序中,我使用 WebClient 调用对远程应用程序的 POST 请求。该方法目前看起来像这样:

// Class A
public void sendNotification(String notification) {
    final WebClient webClient = WebClient.builder()
            .defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
            .build();
    webClient.post()
            .uri("http://localhost:9000/api")
            .body(BodyInserters.fromValue(notification))
            .retrieve()
            .onStatus(HttpStatus::isError, clientResponse -> Mono.error(NotificationException::new))
            .toBodilessEntity()
            .block();
    log.info("Notification delivered successfully");
}

// Class B
public void someOtherMethod() {
    sendNotification("test");
}

用例是:另一个 class 中的方法调用 sendNotification 并且应该处理任何错误,即任何非 2xx 状态或请求甚至无法发送。

但我在 WebClient 中纠结于处理错误的概念。据我了解,以下行将捕获除 2xx/3xx 之外的任何 HTTP 状态,然后是 return 带有 NotificationExceptionMono.error(扩展 Exception 的自定义异常) .

onStatus(HttpStatus::isError, clientResponse -> Mono.error(NotificationException::new))

但是 someOtherMethod() 如何处理这种错误情况?它如何处理这个 Mono.error?或者如果 sendNotification 甚至没有将它扔到签名中,它如何实际捕获 NotificationException

使用 imperative/blocking 样式,您可以用 try-catch 包围它:

try {
    webClient.post()
            .uri("http://localhost:9000/api")
            .body(BodyInserters.fromValue(notification))
            .retrieve()
            .onStatus(HttpStatus::isError, clientResponse -> Mono.error(NotificationException::new))
            .toBodilessEntity()
            .block();
} catch(NotificationException e) {...}

反应性解决方案是像这样使用 onErrorResume 运算符:

    webClient.post()
            .uri("http://localhost:9000/api")
            .body(BodyInserters.fromValue(notification))
            .retrieve()
            .onErrorResume(e -> someOtherMethod())
            .toBodilessEntity()
            .block();

此处,反应式方法someOtherMethod()将在任何错误的情况下执行。

嗯,有很多方法可以处理错误,这实际上取决于您在出现错误时想做什么。

在您当前的设置中,解决方案很简单:首先,NotificationException 应该扩展 RuntimeException,因此,如果出现 HTTP 错误,.block() 会抛出一个 NotificationException。最好将它添加到方法的签名中,并附上 Javadoc 条目。
在另一种方法中,你只需要捕获异常并用它做你想做的事。

/**
 * @param notification
 * @throws NotificationException in case of a HTTP error
 */
public void sendNotification(String notification) throws NotificationException {
    final WebClient webClient = WebClient.builder()
        .defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
        .build();
    webClient.post()
        .uri("http://localhost:9000/api")
        .body(BodyInserters.fromValue(notification))
        .retrieve()
        .onStatus(HttpStatus::isError, clientResponse -> Mono.error(NotificationException::new))
        .toBodilessEntity()
        .block();
    log.info("Notification delivered successfully");
}

public void someOtherMethod() {
    try {
        sendNotification("test");
    } catch (NotificationException e) {
        // Treat exception
    }
}

在更被动的风格中,您可以 return a Mono 并使用 onErrorResume().

public Mono<Void> sendNotification(String notification) {
    final WebClient webClient = WebClient.builder()
        .defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
        .build();
    return webClient.post()
        .uri("http://localhost:9000/api")
        .body(BodyInserters.fromValue(notification))
        .retrieve()
        .onStatus(HttpStatus::isError, clientResponse -> Mono.error(NotificationException::new))
        .bodyToMono(Void.class);
}

public void someOtherMethod() {
    sendNotification("test")
        .onErrorResume(NotificationException.class, ex -> {
            log.error(ex.getMessage());
            return Mono.empty();
        })
        .doOnSuccess(unused -> log.info("Notification delivered successfully"))
        .block();
}