Spring Boot REST服务exception处理

我试图build立一个大规模的REST服务服务器。 我们使用Spring Boot 1.2.1 Spring 4.1.5和Java 8.我们的控制器正在实现@RestController和标准的@RequestMapping注解。

我的问题是,Spring Boot将控制器exception的默认redirect设置为“/ error”。 从文档:

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

从使用Node.js编写REST应用程序的年代来看,对我来说,这是非常明智的。 服务端点生成的任何exception应该在响应中返回。 我不明白为什么你会发送redirect到什么是最有可能的Angular或JQuery SPA消费者只是寻找答案,不能或不会对redirect采取任何行动。

我想要做的是设置一个全局error handling程序,它可以接受任何exception – 有意从请求映射方法抛出,或者由Spring自动生成(404,如果没有find处理程序方法,请求path签名),返回一个标准格式化的错误响应(400,500,503,404)到客户端没有任何MVCredirect。 具体来说,我们要把这个错误logging下来,用UUIDlogging到NoSQL,然后用JSON体中日志条目的UUID返回正确的HTTP错误代码给客户端。

文件在如何做到这一点上是模糊的。 在我看来,您必须创build自己的ErrorController实现或以某种方式使用ControllerAdvice ,但是我所看到的所有示例仍然包括将响应转发给某种错误映射,这没有帮助。 其他的例子表明,你必须列出你想要处理的每个exceptiontypes,而不是只列出“Throwable”并获取所有内容。

任何人都可以告诉我我错过了什么,或者指出我如何做到这一点,而不是build议Node.js更容易处理的链?

附录(4/20/16)

一位匿名用户build议编辑并被拒绝,但他的build议看起来不错,而且很有帮助,所以我添加了它:

使用Spring Boot 1.3.1.RELEASE

新的第1步 –将以下属性添加到application.properties中很容易,也不那么困难:

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

比修改现有的DispatcherServlet实例更容易(如下所示)! – JO'

如果使用完整的RESTful应用程序,禁用静态资源的自动映射是非常重要的,因为如果您使用Spring Boot的默认configuration来处理静态资源,那么资源处理程序将处理请求(它被sorting为最后并映射到/ **这意味着它可以接收任何未被应用程序中的任何其他处理程序处理的请求),所以调度程序servlet没有机会抛出exception。


新答案(12/04/15)

使用Spring Boot 1.2.7.RELEASE

新的第一步 –我发现了一个设置“throExceptionIfNoHandlerFound”标志的less得多的干扰方式。 用你的应用程序初始化类replace下面的(第1步)中的DispatcherServletreplace代码:

 @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框架的任何自动configuration。

还有一件事我发现了@EnableWebMvc注释对Spring Boot是致命的。 是的,这个注释使得像下面描述的那样能够捕获所有的控制器exception,但是它也杀死了Spring Boot通常提供的很多有用的自动configuration。 当您使用Spring Boot时,请特别小心地使用该注释。


原始答案:

经过大量的研究和对这里发布的解决scheme的跟进(感谢您的帮助!)以及Spring代码的运行时追踪,我终于find了一个处理所有exception(不是错误,但是继续阅读)的configuration,包括404s。

第1步 –告诉SpringBoot停止使用MVC“未find处理程序”的情况。 我们希望Spring抛出一个exception,而不是返回给客户端一个视图redirect到“/ error”。 要做到这一点,你需要在你的一个configuration类中有一个入口:

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

这个缺点是它取代了默认的调度程序servlet。 这对我们来说还没有问题,没有任何副作用或执行问题出现。 如果您因为其他原因将使用调度程序servlet执行其他任何操作,则可以使用这个地址。

第2步 –现在,当没有find处理程序时,Spring引导程序将引发exception,那么可以使用统一的exception处理程序中的任何其他exception处理该exception:

 @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启动应用程序现在可以捕获上述处理程序类中的所有exception,包括404s,并且可以随意使用它们。

最后一点 – 似乎没有办法让这个抛出错误。 我有一个古怪的想法,使用方面来捕获错误,并把它们变成上面的代码可以处理的exception,但我还没有时间来真正尝试实现。 希望这有助于某人。

任何意见/更正/增强将不胜感激。

随着春季启动1.4 +新的酷类更容易的exception处理被添加,有助于消除样板代码。

提供了一个新的@RestControllerAdvice用于exception处理,它是@ControllerAdvice@ResponseBody组合。 使用此新注释时,您可以在@ExceptionHandler方法上删除@ResponseBody

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

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

你可以在这里find并使用源代码:
https://github.com/magiccrafter/spring-boot-exception-handling

我认为ResponseEntityExceptionHandler符合你的要求。 HTTP 400的示例代码片段:

 @ControllerAdvice public class MyExceptionHandler extends ResponseEntityExceptionHandler { @ResponseStatus(value = HttpStatus.BAD_REQUEST) @ExceptionHandler({HttpMessageNotReadableException.class, MethodArgumentNotValidException.class, HttpRequestMethodNotSupportedException.class}) public ResponseEntity<Object> badRequest(HttpServletRequest req, Exception exception) { // ... } } 

你可以查看这个post

这个代码呢? 我使用回退请求映射来捕获404错误。

 @Controller @ControllerAdvice public class ExceptionHandlerController { @ExceptionHandler(Exception.class) public ModelAndView exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception ex) { //If exception has a ResponseStatus annotation then use its response code ResponseStatus responseStatusAnnotation = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class); return buildModelAndViewErrorPage(request, response, ex, responseStatusAnnotation != null ? responseStatusAnnotation.value() : HttpStatus.INTERNAL_SERVER_ERROR); } @RequestMapping("*") public ModelAndView fallbackHandler(HttpServletRequest request, HttpServletResponse response) throws Exception { return buildModelAndViewErrorPage(request, response, null, HttpStatus.NOT_FOUND); } private ModelAndView buildModelAndViewErrorPage(HttpServletRequest request, HttpServletResponse response, Exception ex, HttpStatus httpStatus) { response.setStatus(httpStatus.value()); ModelAndView mav = new ModelAndView("error.html"); if (ex != null) { mav.addObject("title", ex); } mav.addObject("content", request.getRequestURL()); return mav; } } 

默认情况下,Spring Boot会给json提供错误信息。

 curl -v localhost:8080/greet | json_pp [...] < HTTP/1.1 400 Bad Request [...] { "timestamp" : 1413313361387, "exception" : "org.springframework.web.bind.MissingServletRequestParameterException", "status" : 400, "error" : "Bad Request", "path" : "/greet", "message" : "Required String parameter 'name' is not present" } 

它也适用于所有types的请求映射错误。 检查这篇文章http://www.jayway.com/2014/10/19/spring-boot-error-responses/

如果你想创build日志到NoSQL。 您可以创build@ControllerAdvice,然后重新抛出exception。 有文档中的例子https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

对于REST控制器,我build议使用Zalando Problem Spring Web

https://github.com/zalando/problem-spring-web

如果Spring Boot的目标是embedded一些自动configuration,这个库为exception处理做了更多的工作。 你只需要添加依赖项:

 <dependency> <groupId>org.zalando</groupId> <artifactId>problem-spring-web</artifactId> <version>LATEST</version> </dependency> 

然后为您的例外定义一个或多个build议特征(或使用默认提供的特征)

 public interface NotAcceptableAdviceTrait extends AdviceTrait { @ExceptionHandler default ResponseEntity<Problem> handleMediaTypeNotAcceptable( final HttpMediaTypeNotAcceptableException exception, final NativeWebRequest request) { return Responses.create(Status.NOT_ACCEPTABLE, exception, request); } } 

然后你可以定义exception处理的控制器build议为:

 @ControllerAdvice class ExceptionHandling implements MethodNotAllowedAdviceTrait, NotAcceptableAdviceTrait { } 

使用dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);解决schemedispatcherServlet.setThrowExceptionIfNoHandlerFound(true);@EnableWebMvc @ControllerAdvice为Spring Boot 1.3.1工作,而不是在1.2.7上工作