Spring 5 Reactive - WebExceptionHandler 没有被调用

Posted

技术标签:

【中文标题】Spring 5 Reactive - WebExceptionHandler 没有被调用【英文标题】:Spring 5 Reactive - WebExceptionHandler is not getting called 【发布时间】:2018-05-17 19:02:27 【问题描述】:

我已经尝试了what is the right way to handle errors in spring-webflux 中建议的所有 3 种解决方案,但 WebExceptionHandler 没有被调用。我正在使用Spring Boot 2.0.0.M7。 Github 仓库here

@Configuration
class RoutesConfiguration 

  @Autowired
  private lateinit var testService: TestService

  @Autowired
  private lateinit var globalErrorHandler: GlobalErrorHandler

  @Bean
  fun routerFunction():

    RouterFunction<ServerResponse> = router 
    ("/test").nest 

      GET("/") 
        ServerResponse.ok().body(testService.test())
      
    
  


 


@Component
class GlobalErrorHandler() : WebExceptionHandler 

  companion object 
    private val log = LoggerFactory.getLogger(GlobalErrorHandler::class.java)
  

  override fun handle(exchange: ServerWebExchange?, ex: Throwable?): Mono<Void> 

    log.info("inside handle")

    /* Handle different exceptions here */
    when(ex!!) 
      is ClientException -> exchange!!.response.statusCode = HttpStatus.BAD_REQUEST
      is Exception -> exchange!!.response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR
    

    return Mono.empty()
  

更新:

当我将 Spring Boot 版本更改为 2.0.0.M2 时,WebExceptionHandler 被调用。我需要为2.0.0.M7做点什么吗?

解决方案:

按照布赖恩的建议,效果不错

@Bean
@Order(-2)
fun globalErrorHandler() = GlobalErrorHandler()

【问题讨论】:

有人知道如何轻松创建 WebExchangeBindException 吗? 【参考方案1】:

您可以提供自己的WebExceptionHandler,但您必须相对于其他人订购,否则他们可能会在您有机会尝试之前处理错误。

Spring Boot 提供的用于错误处理的DefaultErrorWebExceptionHandler (see reference documentation) 订购于-1 Spring Framework 提供的ResponseStatusExceptionHandler 订购地址为0

因此您可以在错误处理组件上添加@Order(-2),以便在现有组件之前对其进行排序。

【讨论】:

Spring boot 内置 AbstractErrorWebExceptionHandler 似乎对基于网页的应用程序的全局错误处理更友好。对于 REST 应用程序,我认为最好提供类似@RestControllerAdvice 的解决方案来设置 http 状态和实体。 我无法让 RestControllerAdvice 在 DefaultErrorWebExceptionHandler 或自定义 WebExceptionHandler 之前捕获错误... @Order(-123) 一定要早点解决问spring context apply bean的问题【参考方案2】:

错误响应应具有标准的有效负载信息。这可以通过扩展AbstractErrorWebExceptionHandler来完成

ErrorResponse:数据类

data class ErrorResponse(
    val timestamp: String,
    val path: String,
    val status: Int,
    val error: String,
    val message: String
)

ServerResponseBuilder:构建错误响应的 2 种不同方法

默认:处理标准错误

webClient:处理 webClient 异常 (WebClientResponseException),不适用于这种情况

class ServerResponseBuilder(
        private val request: ServerRequest, 
        private val status: HttpStatus) 

    fun default(): Mono<ServerResponse> =
        ServerResponse
                .status(status)
                .body(BodyInserters.fromObject(ErrorResponse(
                        Date().format(),
                        request.path(),
                        status.value(),
                        status.name,
                        status.reasonPhrase)))

    fun webClient(e: WebClientResponseException): Mono<ServerResponse> =
        ServerResponse
                .status(status)
                .body(BodyInserters.fromObject(ErrorResponse(
                        Date().format(),
                        request.path(),
                        e.statusCode.value(),
                        e.message.toString(),
                        e.responseBodyAsString)))

GlobalErrorHandlerConfiguration:错误处理程序

@Configuration
@Order(-2)
class GlobalErrorHandlerConfiguration @Autowired constructor(
        errorAttributes: ErrorAttributes,
        resourceProperties: ResourceProperties,
        applicationContext: ApplicationContext,
        viewResolversProvider: ObjectProvider<List<ViewResolver>>,
        serverCodecConfigurer: ServerCodecConfigurer) :
        AbstractErrorWebExceptionHandler(
                errorAttributes,
                resourceProperties,
                applicationContext
        ) 

    init 
        setViewResolvers(viewResolversProvider.getIfAvailable  emptyList() )
        setMessageWriters(serverCodecConfigurer.writers)
        setMessageReaders(serverCodecConfigurer.readers)
    

    override fun getRoutingFunction(errorAttributes: ErrorAttributes?): RouterFunction<ServerResponse> = 
        RouterFunctions.route(RequestPredicates.all(), HandlerFunction<ServerResponse>  response(it, errorAttributes) )

    private fun response(request: ServerRequest, errorAttributes: ErrorAttributes?): Mono<ServerResponse> =
        ServerResponseBuilder(request, status(request, errorAttributes)).default()

    private fun status(request: ServerRequest, errorAttributes: ErrorAttributes?) =
        HttpStatus.valueOf(errorAttributesMap(request, errorAttributes)["status"] as Int)

    private fun errorAttributesMap(request: ServerRequest, errorAttributes: ErrorAttributes?) =
        errorAttributes!!.getErrorAttributes(request, false)

【讨论】:

以上是关于Spring 5 Reactive - WebExceptionHandler 没有被调用的主要内容,如果未能解决你的问题,请参考以下文章

Reactive-Spring-Security-5.1.3.RELEASE,多重授权

Spring 5 Reactive 中的 HTTP 响应异常处理

更新到 Spring 5.1 - java.lang.NoClassDefFoundError: org/springframework/http/server/reactive/ServletSer

在Spring 5中调试Reactive Streams

如何在 Postman 中查看 Spring 5 Reactive API 的响应?

Spring Boot 2. 异步 API。 CompletableFuture vs. Reactive