Spring通过@ControllerAdvice和@ExceptionHandler两个注解即可实现异常的统一处理。
一、用法介绍
@ControllerAdvice和@ExceptionHandler可拦截异常,通过这两个注解可实现对异常的自定义处理。
二、示例
1.需求
提供两种异常返回方式:对RESTful风格返回特定的json格式;对text/html风格,返回特定的页面。
2.实现
第一步:
定义全局异常捕获类
@RestControllerAdvice
public class GlobalExceptionHandle {
private Logger logger = LoggerFactory.getLogger("GlobalExceptionHandler");
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e){
ModelAndView mav = new ModelAndView();
mav.addObject("msg","异常咯");
mav.setViewName("error");
return mav;
}
@ExceptionHandler(BaseException.class)
public Object baseErrorHandler(HttpServletRequest req, Exception e) throws Exception {
logger.error("---BaseException Handler---Host {} invokes url {} ERROR: {}", req.getRemoteHost(), req.getRequestURL(), e.getMessage());
return e.getMessage();
}
}
第二步:
定义异常处理类
public class BaseException extends Exception {
public BaseException(String message) {
super(message);
}
}
第三步:
定义Controller测试异常
@RequestMapping("/ex")
public Object throwIOException() throws Exception {
throw new IOException("This is IOException.");
}
第四步:
为Controller定义特定的异常@ExceptionHandler
在Controller中增加以下代码:
@RequestMapping("/ex5")
public Object throwNullPointerException() throws Exception {
throw new NullPointerException("This is NullPointerException.");
}
@ExceptionHandler(NullPointerException.class)
public String controllerExceptionHandler(HttpServletRequest req, Exception e) {
logger.error("---ControllerException Handler---Host {} invokes url {} ERROR: {}", req.getRemoteHost(), req.getRequestURL(), e.getMessage());
return e.getMessage();
}
Tips:
在SpringBoot中,当用户访问一个不存在的链接时,Spring默认将页面重定向到/error上,而不会抛出异常。既然如此,可以告诉SpringBoot,当出现404错误时,抛出一个异常即可。在application.properties中添加如下代码:
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
三、源码解析
1.默认情况,SpringBoot为两种情况提供了不同的响应方式。
1)浏览器请求一个不存在的页面或服务端发生异常时,一般情况浏览器默认会发送的请求头中Accept: text/html,所以SpringBoot默认会响应一个html文档内容,称作“Whitelabel Error Page”。
2)使用postman工具或者js发送请求一个不存在的url或者服务端发生异常时,SpringBoot会返回如下格式的Json字符串:
2.原理很简单:
SpringBoot默认提供了程序出错的映射路径/error。这个/error请求会在BasicErrorController中处理,其内部是通过判断请求头中的Accept内容是text/html还是其他,来决定返回页面视图还是JSON消息内容。代码如下:
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = this.getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
return modelAndView != null ? modelAndView : new ModelAndView("error", model);
}
@RequestMapping
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = this.getStatus(request);
return new ResponseEntity(body, status);
}
如何自定义Whitelabel Error Page
直接在/resources/template下面创建error.html即可覆盖Whitelabel Error Page的错误页面。如果想定义
四、更多详情
https://segmentfault.com/a/1190000006749441
https://blog.csdn.net/zzz___bj/article/details/80368071
http://tengj.top/2018/05/16/springboot13/#%E4%BB%8B%E7%BB%8DSpring-Boot%E9%BB%98%E8%AE%A4%E7%9A%84%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E6%9C%BA%E5%88%B6