Spring Reactive Web - 异常总是包含在 500 中

Posted

技术标签:

【中文标题】Spring Reactive Web - 异常总是包含在 500 中【英文标题】:Spring Reactive Web - Exceptions are always wrapped in 500 【发布时间】:2020-12-18 10:43:15 【问题描述】:

对于一个非常简单的项目,错误处理不清楚。

public class WebfluxApplication 
    public static void main(String[] args) 
        SpringApplication.run(WebfluxApplication.class, args);
    

public class EndpointRouter


@Bean
public WebClient webClient() 
    return WebClient.builder().build();


@Bean
public RouterFunction<ServerResponse> goodRoute(Handler handler, ConfigProperties configProperties) 
    log.info(configProperties.toString());
    return
            RouterFunctions.route(RequestPredicates.GET("/api/v1/integration/ok"),
                    handler::goodEndpoint)
    .and(
            RouterFunctions.route(RequestPredicates.GET("/api/v1/integration/notfound") 
                    handler::badEndpoint));

public Mono<ServerResponse> goodEndpoint(ServerRequest r) 
    return ServerResponse.ok().build();


public Mono<ServerResponse> badEndpoint(ServerRequest r) 
    var result = service.badEndpoint();
    return ServerResponse
            .ok()
            .body(result, String.class);

public class Service

private final WebClient webClient;
private final ConfigProperties configProperties;

public Service(WebClient webClient, ConfigProperties configurationProperties) 
    this.webClient = webClient;
    this.configProperties = configurationProperties;


public Mono<String> badEndpoint() 
    return webClient
            .get()
            .uri(configProperties.getNotfound())
            .retrieve()
            .onStatus(HttpStatus::is4xxClientError, clientResponse -> 
                if(clientResponse.statusCode().equals(HttpStatus.NOT_FOUND))
                    return Mono.error(new HttpClientErrorException(HttpStatus.NOT_FOUND,
                            "Entity not found."));
                 else 
                    return Mono.error(new HttpClientErrorException(HttpStatus.INTERNAL_SERVER_ERROR));
                
            )
            .bodyToMono(String.class);

通过阅读文档,我不需要为整个项目设置全局错误处理程序,我应该能够处理 404,并将 404 返回给原始调用者。

这是输出

   2020-08-29 16:52:46.301 ERROR 25020 --- [ctor-http-nio-4] a.w.r.e.AbstractErrorWebExceptionHandler : [b339763e-1]  500 Server Error for HTTP GET "/api/v1/integration/notfound"

org.springframework.web.client.HttpClientErrorException: 404 Entity not found.
    at com.stevenpg.restperformance.webflux.Service.lambda$badEndpoint$0(Service.java:30) ~[main/:na]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ 404 from GET https://httpbin.org/status/404 [DefaultWebClient]
    |_ checkpoint ⇢ Handler com.stevenpg.restperformance.webflux.EndpointRouter$$Lambda$445/0x00000008003ae040@7799b58 [DispatcherHandler]
    |_ checkpoint ⇢ HTTP GET "/api/v1/integration/notfound" [ExceptionHandlingWebHandler]

我也尝试在服务中的 Mono 上使用 onErrorResume,但它永远无法正常工作,并且需要返回 Mono 而不是 Mono。

文档和 Stack Overflow 没有很多/任何在 RouterFunction 内进行 WebClient 调用和处理不同类型响应的示例。

【问题讨论】:

为什么需要它来处理 badRequest。默认情况下,在 Spring Boot 中,非映射 url 返回 404 错误。除了你的 500 错误是由于你的 /api/v1/integration/notfound 没有过时。从 WebClient 调用时,它应该类似于 localhost:8080/api/v1/integration/notfound。但这一次会有循环。你的目的是什么? 从错误中可以看出,404来自httpbin.org/status/404。我正在尝试处理来自外部客户端的 404。如果我们的端点调用另一个端点,它应该能够处理任何状态码。 /ok 端点工作正常。 【参考方案1】:

只需添加 onErrorResume 即可解决您的问题。否则在 AbstractErrorWebExceptionHandler 中处理错误。

 return webClient
        .get()
        .uri(configProperties.getNotfound())
        .retrieve()
        .onStatus(HttpStatus::is4xxClientError, clientResponse -> 
            if(clientResponse.statusCode().equals(HttpStatus.NOT_FOUND))
                return Mono.error(new HttpClientErrorException(HttpStatus.NOT_FOUND,
                        "Entity not found."));
             else 
                return Mono.error(new HttpClientErrorException(HttpStatus.INTERNAL_SERVER_ERROR));
            
        )
        .bodyToMono(String.class)
        .onErrorResume( e -> Mono.just(e.getMessage()) );

【讨论】:

我完全误解了错误处理的工作原理,这非常有效!谢谢!

以上是关于Spring Reactive Web - 异常总是包含在 500 中的主要内容,如果未能解决你的问题,请参考以下文章

无法解析 spring-web-reactive 的依赖项

Spring boot加载REACTIVE程序过程

Please set spring.main.web-application-type=reactive or remove spring-boot-starter-web dependency.

Spring Reactive Web 更新方法创建新记录而不是更新现有记录

Please set spring.main.web-application-type=reactive or remove spring-boot-starter-web dependency(代码

Spring WebFlux Reactive 和 Kotlin Coroutines 启动错误