spring - 如何全局处理 Spring WebFlux 中 WebFilter 抛出的错误?

标签 spring exception spring-webflux

当错误从 WebFilter 链中抛出时,如何在 WebFlux 中全局拦截和处理错误?

很清楚如何处理 Controller 抛出的错误:@ControllerAdvice@ExceptionHandler帮助很大。

WebFilter 组件抛出异常时,此方法不起作用。

在以下配置中,GET/firstGET/second 响应有意引发异常抛出。尽管@ExceptionHandler 方法handleFirsthandleSecond 类似,但handleSecond 永远不会被调用。我想这是因为 MyWebFilter 不允许 ServerWebExchange 进入可以应用 GlobalErrorHandlers 方法的阶段。

响应 GET/first:

 HTTP 500 "hello first"                   // expected
 HTTP 500 "hello first"                   // actual

响应 GET/秒:

 HTTP 404 "hello second"                                                         // expected
 HTTP 500 {"path": "/second", "status": 500, "error": "Internal Server Error" }  // actual

@RestController
class MyController {

    @GetMapping("/first")
    String first(){
        throw new FirstException("hello first");
    }
}


@Component
class MyWebFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange swe, WebFilterChain wfc) {
        var path = swe.getRequest().getURI().getPath();
        if (path.contains("second")){
            throw new SecondException("hello second")
        }
    }
}


@ControllerAdvice
class GlobalErrorHandlers {

    @ExceptionHandler(FirstException::class)
    ResponseEntity<String> handleFirst(FirstException ex) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.message)
    }

    @ExceptionHandler(SecondException::class)
    ResponseEntity<String> handleSecond(SecondException ex) {
       return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.message)
    }
}

最佳答案

需要三个步骤才能完全控制从应用程序端点处理代码抛出的所有异常:

  1. 实现org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler
  2. 使用 @ControllerAdvice(或仅 @Component)进行注释
  3. @Priority 设置为小于 1,以使自定义处理程序在默认处理程序 (WebFluxResponseStatusExceptionHandler) 之前运行

The tricky part is where we get an instance implementing ServerResponse.Context for passing to ServerResponse.writeTo(exchange, context). I did not find the final answer, and comments are welcome. In the internal Spring code they always create a new instance of context for each writeTo invocation, although in all cases (I've manged to find) the context instance is immutable. That is why I ended up using the same ResponseContextInstance for all responses. At the moment no problems detected with this approach.


@ControllerAdvice
@Priority(0) /* should go before WebFluxResponseStatusExceptionHandler */
class CustomWebExceptionHandler : ErrorWebExceptionHandler { 

    private val log = logger(CustomWebExceptionHandler::class)

    override fun handle(exchange: ServerWebExchange, ex: Throwable): Mono<Void> {
        log.error("handled ${ex.javaClass.simpleName}", ex)

        val sr = when (ex) {
            is FirstException -> handleFirst(ex) 
            is SecondException -> handleSecond(ex) 
            else -> defaultException(ex)
        }

        return sr.flatMap { it.writeTo(exchange, ResponseContextInstance) }.then()
    }

    private fun handleFirst(ex: FirstException): Mono<ServerResponse> {
        return ServerResponse
                   .status(HttpStatus.INTERNAL_SERVER_ERROR)
                   .bodyValue("first")
    }

    private fun handleSecond(ex: SecondException): Mono<ServerResponse> {
        return ServerResponse.status(HttpStatus.BAD_REQUEST).bodyValue("second")
    }

    private object ResponseContextInstance : ServerResponse.Context {

        val strategies: HandlerStrategies = HandlerStrategies.withDefaults()

        override fun messageWriters(): List<HttpMessageWriter<*>> {
            return strategies.messageWriters()
        }

        override fun viewResolvers(): List<ViewResolver> {
            return strategies.viewResolvers()
        }
    }
}

关于spring - 如何全局处理 Spring WebFlux 中 WebFilter 抛出的错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70873751/

相关文章:

javascript - 在 If 条件中使用 window.location 在 CSS 文件之间切换

Java servlet 返回 404 错误

spring-boot - WebClient 的 bodyToMono 关于空体预期行为

java - 无法在spring mvc中使用css/js等资源

java - 在应用程序启动时缓存查找不起作用

Angular2 缺少 Set-Cookie 作为对 REST 服务的响应

c# - 为什么这会导致 'Index out of range' 异常?

c# - C# 中的全局异常处理(控制台应用程序)

java - 如何拦截使用 WebClient/WebFlux 形成的所有传出 HTTP 请求?

Spring Webflux带注释的rest Controller 不支持ServerHttpRequest作为方法参数: java. lang.NoSuchMethodException