Spring Webflux的业务领域异常机制

18-11-15 banq
              

最近我被分配了一个任务:在SpringBoot的Web应用中实现异常处理机制。就像几乎所有SpringBoot项目一样, 有许多不同的方法来实现它。以下是我的研究结果和最终解决方案。

需求

不同需求如下:

#1返回的错误消息必须与框架提供的格式兼容,特别是在JSON中,格式为:

{
    "timestamp": 1539797674906,
    "path": "/",
    "status": 418,
    "error": "I'm a teapot",
    "message": "Teapot Mike!"
}

出于多种原因,这种兼容性很重要。

首先,API使用者始终使用相同的错误格式

其次,仍然可以使用JSR-303 / JSR-348 Bean验证机制,我认为输入验证应该在域本身中进行,尽可能接近领域模型。然而,@NotNull等一些注释字段将让我们拒绝传入的请求因而无需进一步处理。如果发生验证错误,则会在响应中添加一个附加字段 -  errors,这是映射到BindingResult对象的。

再者,现在有一种趋势是将应用程序代码尽可能地与框架分开。虽然我知道这个目的,但我也意识到应用程序是沉浸在框架中 - 在这种情况下,我发现使用框架机制是合理的。

#2应用程序中抛出的所有异常都应自动转换为相应的HTTP状态代码。

有人可能会想,我是想在应用程序中引入基于异常的通信。当然不是。我希望所有的异常都可以在视图层(@Controller)中解决。

#3所有异常应该从一个基类扩展(DomainException)

#4应该有一个集中的点,可以捕获和处理所有异常。另外,如果可以将这样的异常处理程序透明地注入到应用程序中,那将会很棒。

#5我想在错误响应中添加一个额外的字段 -  traceId。在基于微服务的应用程序中,它使调试问题更少。

实现目标

要在基于Spring Webflux的应用程序中处理异常,您可以:

  1. 使用本地@ExceptionHandler,扩展ResponseStatusException或使用@ResponseStatus注释的异常类。
  2. 提供带注释的自定义类@ControllerAdvice
  3. 提供扩展的自定义类DefaultErrorAttributes

该文批判了前面两个点,理由非常饶人,这里不做详细阐述,文章最后推荐第三点:

第三点满足所有要求:

满足#1格式兼容性 - 正如我之前所说,在Spring Framework中没有代表错误响应的类,它只是一个Map。

对于#2和#3,我们有一个可扩展的DomainException - 一切都很好!

#4 DomainExceptionWrapper是一个可注入的单个组件,可以作为工件发布到maven存储库,并作为依赖项添加到其他应用程序。当上下文启动时,它将被自动检测并作为bean注入。它也很容易测试。

#5由于所有(域,运行时,Spring)异常都在一个地方处理,而旧的异常被Map用作返回的对象,因此很容易从中添加或删除字段。