错误处理机制
Posted beanbag
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了错误处理机制相关的知识,希望对你有一定的参考价值。
1、默认的错误处理机制
SpringBoot在页面请求发生错误的时候,会返回一个默认的页面,如下图:
浏览器做为客户端
如果使用的是浏览器做为客户端来访问接口,那么在浏览器的请求头中会有
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
其他客户端
Accept: */*
默认响应一个Json数据
{ "timestamp": 1538381770900, "status": 404, "error": "Not Found", "message": "No message available", "path": "/crud/aaa" }
2、错误处理的自动配置
错误处理的自动配置类:ErrorMvcAutoConfiguration
给容器中添加了如下的组件
- DefaultErrorAttributes
在页面中共享信息
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) { Map<String, Object> errorAttributes = new LinkedHashMap(); errorAttributes.put("timestamp", new Date()); this.addStatus(errorAttributes, requestAttributes); this.addErrorDetails(errorAttributes, requestAttributes, includeStackTrace); this.addPath(errorAttributes, requestAttributes); return errorAttributes; }
- BasicErrorController
处理默认/error请求,如果客户端为web浏览器,用下面的方法处理,产生html类型的数据。
@RequestMapping( produces = {"text/html"} ) 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); }
如果是其他的客户端,用下面的方法处理返回json数据
@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); }
- ErrorPageCustomizer
系统出现错误后来到error请求进行处理。
@Value("${error.path:/error}") private String path = "/error";
- DefaultErrorViewResolver
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) { ModelAndView modelAndView = this.resolve(String.valueOf(status), model); if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) { modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model); } return modelAndView; } private ModelAndView resolve(String viewName, Map<String, Object> model) { //默认SpringBoot会去找到一个页面 /error/404 String errorViewName = "error/" + viewName; //如果模版引擎可以解析这个页面地址就用这个模版引擎解析 TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext); //模版引擎解析后返回errorViewName指定的地址 //模版引擎不可以使用就去静态资源文件夹下面找/error/404.html页面 return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model); }
3、配置步骤
一但系统出现4xx或者5xx之类的错误;ErrorPageCustomizer就会生效(定制错误的响应规则);就会来到/error请求;就会被BasicErrorController处理;去哪个页面是由DefaultErrorViewResolver解析得到的
定制错误页面
1)、有模板引擎的情况下:error/状态码; 【将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的 error文件夹下】,发生此状态码的错误就会来到对应的页面;
我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html);
页面能获取的信息
- timestamp:时间戳
- status:状态码
- error:错误提示
- exception:异常对象
- message:异常消息
- errors:JSR303数据校验的错误都在这里
采用模版引擎的情况下,可以使用下面的写法来获取信息
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<h1>4xx</h1>
<h1>[[${timestamp}]]</h1>
<h1>[[${status}]]</h1>
<h1>[[${error}]]</h1>
<h1>[[${exception}]]</h1>
<h1>[[${message}]]</h1>
</main>
2)、没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找;
3)、以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面;
定制错误的json数据
自定义一个异常
public class UserNotExistException extends RuntimeException { public UserNotExistException() { super("用户不存在"); } }
Controller中
@ResponseBody @RequestMapping("/hello") public String hello(@RequestParam("user") String user){ if(user.equals("aaa")){ throw new UserNotExistException(); } return "HelloWorld"; }
页面
<h1>5xx</h1> <h1>[[${timestamp}]]</h1> <h1>[[${status}]]</h1> <h1>[[${error}]]</h1> <h1>[[${exception}]]</h1> <h1>[[${message}]]</h1>
效果
{ "timestamp": 1538453996437, "status": 500, "error": "Internal Server Error", "exception": "cn.zhuangxp.springboot.exception.UserNotExistException", "message": "用户不存在", "path": "/crud/hello" }
以上返回的json数据是SpringBoot帮助我们定义好的,我们还可以定义自己的异常json信息。
@ControllerAdvice public class MyExceptionhandler { @ResponseBody @ExceptionHandler(UserNotExistException.class) public Map<String, Object> handleException(Exception e){ Map<String,Object> map = new HashMap<>(); map.put("code","user.notexist"); map.put("msg",e.getMessage()); return map; } }
效果
{ "msg": "用户不存在", "code": "user.notexist" }
上面的异常处理没有自适应效果。自适应效果要求如果是浏览器访问返回的是页面,如果是其他客户端访问返回的是json数据。
如何变成自适应效果呢?
/** * 存在自适应效果 * @param e * @return */ @ExceptionHandler(UserNotExistException.class) public String handleException(Exception e, HttpServletRequest request){ Map<String,Object> map = new HashMap<>(); request.setAttribute("javax.servlet.error.status_code",500); map.put("code","user.notexist"); map.put("message","用户出错啦"); //将上面的错误信息map放在请求域中 request.setAttribute("ext",map); return "forward:/error"; }
编写一个自定义错误属性类,并添加到容器中
@Component public class MyErrorAttributes extends DefaultErrorAttributes { @Override public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) { Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace); Map<String,Object> ext = (Map<String, Object>) requestAttributes.getAttribute("ext", 0); //异常处理器携带的数据 map.put("name","zxp"); map.put("ext",ext); return map; } }
这样写之后,用浏览器访问的时候,发生错误会跳转到我们设置好的页面上。用其他的浏览器就会返回相应的json错误信息。
{ "timestamp": 1538466112389, "status": 500, "error": "Internal Server Error", "exception": "cn.zhuangxp.springboot.exception.UserNotExistException", "message": "用户不存在", "path": "/crud/hello", "name": "zxp", "ext":{ "code": "user.notexist", "message": "用户出错啦" } }
以上是关于错误处理机制的主要内容,如果未能解决你的问题,请参考以下文章