Spring为REST实现异常处理

在Spring 3.2之前,在Spring MVC应用程序中处理异常的两种主要方法是:HandlerExceptionResolver或@ExceptionHandler注释。这两个都有一些明显的缺点。3.2之后,我们现在有了新的@ControllerAdvice注释来解决前面两个解决方案的局限性。所有这些都有一个共同点 - 他们处理分离问题非常好。应用程序可以正常抛出异常以指示某种类型的异常 - 然后将单独处理异常。

解决方案1 - 控制器级别@ExceptionHandler

在@Controller class中定义一个方法来处理异常, 并加上@ExceptionHandler annotation:

1
2
3
4
5
6
7
8
@RestController
public class FooController{
//...
@ExceptionHandler({ CustomException1.class, CustomException2.class })
public void handleException() {
//
}
}

主要缺点: @ExceptionHandler注释方法只对该该Controller有效, 不能全局使用。

解决方案2 - HandlerExceptionResolver

定义一个 HandlerExceptionResolver 统一处理决应用程序抛出的任何异常。

Spring 3.1 ExceptionHandlerExceptionResolve

默认在DispatcherServlet中启用, @ExceptionHandler就是通过它实现的

Spring 3.0 DefaultHandlerExceptionResolver

默认在DispatcherServlet中启用, 他会将Spring的异常解析为相应的HTTP status codes, e.g. 400, 500 …
完整的异常和对应的HTTP status code, 但是他没有设置任何的response body.

Spring 3.0 ResponseStatusExceptionResolver

默认在DispatcherServlet中启用, 自定义异常的@ResponseStatus注释,并将这些异常映射到HTTP状态代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@ResponseStatus(value = HttpStatus.NOT_FOUND)
publi class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException() {
super();
}
public ResourceNotFoundException(String message, Throwable cause) {
super(message, cause);
}
public ResourceNotFoundException(String message) {
super(message);
}
public ResourceNotFoundException(Throwable cause) {
super(cause);
}
}

自定义HandlerExceptionResolver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Component
public class RestResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver {

@Override
protected ModelAndView doResolveException
(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try {
if (ex instanceof IllegalArgumentException) {
return handleIllegalArgument((IllegalArgumentException) ex, response, handler);
}
...
} catch (Exception handlerException) {
logger.warn("Handling of [" + ex.getClass().getName() + "]
resulted in Exception", handlerException);
}
return null;
}

private ModelAndView handleIllegalArgument
(IllegalArgumentException ex, HttpServletResponse response) throws IOException {
response.sendError(HttpServletResponse.SC_CONFLICT);
String accept = request.getHeader(HttpHeaders.ACCEPT);
...
return new ModelAndView();
}
}

解决方案3 - 使用@ControllerAdvice(@RestControllerAdvice)注解(需要Spring 3.2及以上de版本)

@ControllerAdvice注释来支持全局@ExceptionHandler。

1
2
3
4
5
6
7
8
9
10
11
@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

@ExceptionHandler(value = { IllegalArgumentException.class, IllegalStateException.class })
@ResponseBody
protected ResponseEntity<Object> handleConflict(RuntimeException ex, WebRequest request) {
String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse,
new HttpHeaders(), HttpStatus.CONFLICT, request);
}
}

解决@ExceptionHandler不能全局处理。

原文链接: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/web.html#mvc-ann-exceptionhandler

欢迎关注我的其它发布渠道