错误处理机制

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": "用户出错啦"
    }
}

 


以上是关于错误处理机制的主要内容,如果未能解决你的问题,请参考以下文章

异常和TCP通讯

如何向 ViewPager 提供许多片段并避免错误代码?

Go错误处理机制及自定义错误

深入理解C++中的异常处理机制

PHP中的错误处理机制

Java异常处理机制