弹簧引导 REST 服务异常处理

我正在尝试设置一个大型 REST 服务服务器。我们使用的是Spring Boot 1.2.1 Spring 4.1.5和Java 8。我们的控制器正在实施@RestController和标准@RequestMapping注释。

我的问题是Spring Boot为控制器异常设置了一个默认重定向。从文档中:/error

默认情况下,Spring Boot 提供了一个 /error 映射,该映射以合理的方式处理所有错误,并在 servlet 容器中注册为“全局”错误页面。

多年来,我用Node.js编写REST应用程序,对我来说,这绝不是明智的。服务终结点生成的任何异常都应在响应中返回。我不明白为什么你会发送一个重定向到最有可能是Angular或JQuery SPA消费者的地方,他们只是在寻找答案,不能或不会对重定向采取任何行动。

我想做的是设置一个全局错误处理程序,它可以采取任何异常 - 从请求映射方法有意抛出或由Spring自动生成(如果没有找到请求路径签名的处理程序方法,则为404),并向客户端返回标准格式化的错误响应(400,500,503,404),而无需任何MVC重定向。具体来说,我们将采用错误,将其记录到NoSQL,并使用UUID将其返回给客户端,其中包含JSON正文中日志条目的UUID的正确HTTP错误代码。

文档在如何执行此操作方面一直含糊不清。在我看来,你必须创建自己的ErrorController实现或以某种方式使用ControrOrAdvice,但是我看到的所有示例仍然包括将响应转发到某种错误映射,这无济于事。其他示例表明,您必须列出要处理的每个异常类型,而不仅仅是列出“可抛出”并获取所有内容。

谁能告诉我我错过了什么,或者为我指出正确的方向,如何做到这一点,而不建议Node.js更容易处理的链条?


答案 1

新答案 (2016-04-20)

使用 Spring Boot 1.3.1.RELEASE

新步骤 1 -将以下属性添加到应用程序属性非常简单且侵入性较小:

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

比修改现有的 DispatcherServlet 实例(如下所示)要容易得多!- 乔

如果使用完整的RESTful应用程序,禁用静态资源的自动映射非常重要,因为如果您使用Spring Boot的默认配置来处理静态资源,那么资源处理程序将处理该请求(它是最后排序的并映射到/**,这意味着它拾取应用程序中任何其他处理程序尚未处理的任何请求),因此调度程序servlet没有机会引发异常。


新答案 (2015-12-04)

使用 Spring Boot 1.2.7.RELEASE

新步骤 1 -我发现了一种侵入性小得多的方法来设置“throExceptionIfNoHandlerFound”标志。将下面的 DispatcherServlet 替换代码(步骤 1)替换为应用程序初始化类中的 this 代码:

@ComponentScan()
@EnableAutoConfiguration
public class MyApplication extends SpringBootServletInitializer {
    private static Logger LOG = LoggerFactory.getLogger(MyApplication.class);
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
        DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    }

在本例中,我们在现有的 DispatcherServlet 上设置了标志,它保留了 Spring Boot 框架的任何自动配置。

我发现的另一件事 - @EnableWebMvc注释对Spring Boot来说是致命的。是的,该注释可以像下面描述的那样捕获所有控制器异常,但它也扼杀了Spring Boot通常提供的许多有用的自动配置。使用Spring Boot时要格外小心地使用该注释。


原始答案:

经过更多的研究和跟进这里发布的解决方案(感谢您的帮助!)以及不小的运行时跟踪到Spring代码中,我终于找到了一个配置,可以处理所有异常(不是错误,而是继续阅读),包括404。

步骤1 - 告诉SpringBoot停止使用MVC用于“找不到处理程序”的情况。我们希望Spring抛出一个异常,而不是将视图重定向到“/error”返回给客户端。为此,您需要在其中一个配置类中有一个条目:

// NEW CODE ABOVE REPLACES THIS! (2015-12-04)
@Configuration
public class MyAppConfig {
    @Bean  // Magic entry 
    public DispatcherServlet dispatcherServlet() {
        DispatcherServlet ds = new DispatcherServlet();
        ds.setThrowExceptionIfNoHandlerFound(true);
        return ds;
    }
}

这样做的缺点是它取代了默认的调度程序 servlet。这对我们来说还不是问题,没有出现任何副作用或执行问题。如果您出于其他原因要对调度程序 servlet 执行任何其他操作,则可以在此处执行这些操作。

第 2 步 -现在,当找不到处理程序时,spring boot 将引发异常,因此可以在统一的异常处理程序中与任何其他异常一起处理该异常:

@EnableWebMvc
@ControllerAdvice
public class ServiceExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    ResponseEntity<Object> handleControllerException(HttpServletRequest req, Throwable ex) {
        ErrorResponse errorResponse = new ErrorResponse(ex);
        if(ex instanceof ServiceException) {
            errorResponse.setDetails(((ServiceException)ex).getDetails());
        }
        if(ex instanceof ServiceHttpException) {
            return new ResponseEntity<Object>(errorResponse,((ServiceHttpException)ex).getStatus());
        } else {
            return new ResponseEntity<Object>(errorResponse,HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Map<String,String> responseBody = new HashMap<>();
        responseBody.put("path",request.getContextPath());
        responseBody.put("message","The URL you have reached is not in service at this time (404).");
        return new ResponseEntity<Object>(responseBody,HttpStatus.NOT_FOUND);
    }
    ...
}

请记住,我认为“@EnableWebMvc”注释在这里很重要。似乎没有它,这一切都不起作用。就是这样 - 您的Spring启动应用程序现在将捕获上述处理程序类中的所有异常,包括404,您可以随意使用它们。

最后一点 - 似乎没有办法让它捕获抛出的错误。我有一个古怪的想法,即使用方面来捕获错误并将其转换为上述代码可以处理的异常,但我还没有时间实际尝试实现它。希望这有助于某人。

任何评论/更正/增强将不胜感激。


答案 2

使用Spring Boot 1.4 +添加了新的很酷的类,以便更轻松地处理异常,这有助于删除样板代码。

为异常处理提供了一个新的,它是 和 的组合。使用此新批注时,可以删除 on 方法。@RestControllerAdvice@ControllerAdvice@ResponseBody@ResponseBody@ExceptionHandler

@RestControllerAdvice
public class GlobalControllerExceptionHandler {

    @ExceptionHandler(value = { Exception.class })
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiErrorResponse unknownException(Exception ex, WebRequest req) {
        return new ApiErrorResponse(...);
    }
}

为了处理 404 错误,向 application.properties 添加注释和以下内容就足够了:@EnableWebMvc
spring.mvc.throw-exception-if-no-handler-found=true

您可以在这里找到并尝试使用源代码:
https://github.com/magiccrafter/spring-boot-exception-handling


推荐