错误处理和定制错误页面

配置原理:

可以参照ErrorMvcAutoConfiguration:错误处理的自动配置

给容器添加了如下组件:

DefaultErrorAttributes

   @Bean
    @ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
    public DefaultErrorAttributes errorAttributes() {
        return new DefaultErrorAttributes();
    }

 

BasicErrorController  :  处理默认/error请求

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController{
  
  @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)//产生html类型的数据,浏览器发送的请求来到这个方法处理
   public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
      HttpStatus status = getStatus(request);
      Map<String, Object> model = Collections
                .unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
      response.setStatus(status.value());
     //去哪个页面作为错误页面,包含页面地址和内容
      ModelAndView modelAndView = resolveErrorView(request, response, status, model);
      return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
   }
 
   @RequestMapping//产生json数据,其他客户端来这里
   public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
      HttpStatus status = getStatus(request);
      if (status == HttpStatus.NO_CONTENT) {
         return new ResponseEntity<>(status);
      }
      Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
      return new ResponseEntity<>(body, status);
   }

}

 

ErrorPageCustomizer

@Value("${error.path:/error}")
private String path="/error";//系统出现错误来到error请求进行处理,选择异常处理页面所在文件夹

 

defaultErrorViewResolver

  @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
       ModelAndView modelAndView
= resolve(String.valueOf(status.value()), model); if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) { modelAndView = resolve(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); if (provider != null) {
        //模板引擎可用的情况下返回errorViewName指定的视图地址
return new ModelAndView(errorViewName, model); }
      //模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面
return resolveResource(errorViewName, model); }
  private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
    for (String location : this.resourceProperties.getStaticLocations()) {
       try {
          Resource resource = this.applicationContext.getResource(location);
          resource = resource.createRelative(viewName + ".html");
          if (resource.exists()) {
        return new ModelAndView(new HtmlResourceView(resource), model);
          }
       }catch (Exception ex) {
     }
   }
   return null;
 }

 

一旦发生错误,ErrorPageCustomizer就会发生作用,就会来到error下;就会被BasicErrorController 处理:

1)响应页面:

  去哪个页面是由defaultErrorViewResolver决定的

  

@Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
        if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
            modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
        }
        return modelAndView;
    }

2)指定错误页面:

  有模板引擎的情况下:根据error status状态码来到Templates文件下error文件夹中找对应的页面

  没有的话,在static静态资源文件夹找

 

自定义异常处理: 

   @ResponseBody
    @org.springframework.web.bind.annotation.ExceptionHandler(NullUserException.class)
    public Map<String, String> handleNullUserException(Exception e) {
        Map<String, String> map = new HashMap<>();
        map.put("name", "miao");
        map.put("age", "23");
        return map;
    }
没有自适应效果
@ResponseBody
    @org.springframework.web.bind.annotation.ExceptionHandler(NullUserException.class)
    public String handleNullUserException(Exception e, HttpServletRequest request) {
        // 一定要有自己的错误状态码
        request.setAttribute("javax.servlet.error.status_code", 400);
        Map<String, String> map = new HashMap<>();
        map.put("name", "miao");
        map.put("age", "23");
        return "forward:/error";
    }
有自适应效果
定制自己的异常数据:
出现错误会后,会来到error,会被 BasicErrorControllrt 处理,响应出去的数据是由getErrorAttributes得到的
1,我们可以编写一个ErrorController的实现类,或者AbstractErrorController的子类,放在容器中

//@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
 @Override
 public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
  Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
  if ((Integer) map.get("status") == 500) {
   map.put("message", "服务器内部错误!");
  }
  return map;
 }
}
//定义好的 ErrorAttributes 一定要注册成一个 Bean ,这样,Spring Boot 就不会使用默认的 DefaultErrorAttributes 了

 

自定义异常视图解析
@Component
public class MyErrorViewResolver extends DefaultErrorViewResolver { public MyErrorViewResolver(ApplicationContext applicationContext, ResourceProperties resourceProperties) { super(applicationContext, resourceProperties); } /** * */ @Override public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) { return new ModelAndView("/aaa/123", model); } }
实际上,开发者也可以在这里定义异常数据(直接在 resolveErrorView 方法重新定义一个 model ,将参数中的model 数据拷贝过去并修改,注意参数中的 model 类型为 UnmodifiableMap,即不可以直接修改),而不需要自定义MyErrorAttribute

 

posted @ 2020-10-02 16:17  墨冰心  阅读(145)  评论(0编辑  收藏  举报