14、SpringBoot-CRUD错误处理机制(1)

一、springboot默认的处理机制

1.浏览器返回一个错误的页面
默认处理错误:返回一个错误的页面:
包括错误类型、时间......

14、SpringBoot-CRUD错误处理机制(1)

14、SpringBoot-CRUD错误处理机制(1)

 
2.其他客户端访问
默认响应一个json数据

14、SpringBoot-CRUD错误处理机制(1)

14、SpringBoot-CRUD错误处理机制(1)

原理:

错误自动配置的类:ErrorMvcAutoConfiguration.java

14、SpringBoot-CRUD错误处理机制(1)

14、SpringBoot-CRUD错误处理机制(1)

14、SpringBoot-CRUD错误处理机制(1)

默认配置:

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


@Bean
@ConditionalOnMissingBean(
    value = {ErrorController.class},
    search = SearchStrategy.CURRENT
)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
    return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);
}

@Bean
public ErrorMvcAutoConfiguration.ErrorPageCustomizer errorPageCustomizer() {
    return new ErrorMvcAutoConfiguration.ErrorPageCustomizer(this.serverProperties, this.dispatcherServletPath);
}

@Bean
@ConditionalOnBean({DispatcherServlet.class})
@ConditionalOnMissingBean
public DefaultErrorViewResolver conventionErrorViewResolver() {
    return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
}

 1、DefaultErrorAttributes:

在页面共享信息
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
    Map<String, Object> errorAttributes = new LinkedHashMap();
    errorAttributes.put("timestamp", new Date());
    errorAttributes.put("path", request.path());
    Throwable error = this.getError(request);
    HttpStatus errorStatus = this.determineHttpStatus(error);
    errorAttributes.put("status", errorStatus.value());
    errorAttributes.put("error", errorStatus.getReasonPhrase());
    errorAttributes.put("message", this.determineMessage(error));
    this.handleException(errorAttributes, this.determineException(error), includeStackTrace);
    return errorAttributes;
}

 2、BasicErrorController:处理默认/error请求

@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {

@RequestMapping(
    produces = {"text/html"}//产生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);
}

@RequestMapping   //产生json数据,其他客户端来到这个方法处理;
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);
}
...
}

3、ErrorPageCustomizer:

public class ErrorProperties {
@Value("${error.path:/error}")
private String path = "/error";
public String getPath() {
    return this.path;
}
...
}

4、DefaultErrorViewResolver:

public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
    ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), 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对应的页面
    return provider != null ? new ModelAndView(errorViewName, model) :
 this.resolveResource(errorViewName, model);
}

步骤:

一但系统出现4xx或者5xx之类的错误;ErrorPageCustomizer就会生效(定制错误的响应规则);
就会来到/error请求,就会被BasicErrorController进行处理
1)响应页面(resolveErrorView方法)去那个页面是由DefaultErrorViewResolver解析得到的
protected ModelAndView resolveErrorView(HttpServletRequest request, 
HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
    //所有的ErrorViewResolver得到ModelAndView
    Iterator var5 = this.errorViewResolvers.iterator();
    ModelAndView modelAndView;
    do {
        if (!var5.hasNext()) {
            return null;
        }
        ErrorViewResolver resolver = (ErrorViewResolver)var5.next();
        modelAndView = resolver.resolveErrorView(request, status, model);
    } while(modelAndView == null);
    return modelAndView;
}