SpringBoot错误处理
SpringBoot错误处理
1 SpringMVC写法
1.1 在单独的Controller写一个处理异常的方法处理
@Slf4j
@RestController
public class HelloController {
@GetMapping("/exception")
public String exception(){
int i = 1 / 0;
return "异常";
}
/**
* 只能处理当前Controller发生的错误
*/
@ExceptionHandler
public String handlerException(Exception e){
return "exception...." + e.getMessage();
}
}
1.2 写一个异常处理类,使用@ControllerAdvice
注解处理所有的Controller异常
public class GlobalExceptionHandler {
@ResponseBody
@ExceptionHandler(Exception.class)
public String handlerException(Exception e){
return "发生错误,原因:" + e.getMessage();
}
}
2. SpringBoot处理流程分析
2.1 默认机制
错误处理的自动配置都在
ErrorMvcAutoConfiguration
中,两大核心机制:
- SpringBoot 会自适应处理错误,
响应页面
或JSON数据
- SpringMVC的错误处理机制依然保留,MVC处理不了,才会交给boot进行处理
- 解析一个错误页
a. 如果发生了500、404、503、403 这些错误
ⅰ. 如果有模板引擎,默认在 classpath:/templates/error/精确码.html
ⅱ. 如果没有模板引擎,在静态资源文件夹下找 精确码.html
b. 如果匹配不到精确码.html这些精确的错误页,就去找5xx.html,4xx.html模糊匹配
ⅰ. 如果有模板引擎,默认在 classpath:/templates/error/5xx.html
ⅱ. 如果没有模板引擎,在静态资源文件夹下找 5xx.html - 如果模板引擎路径templates下有 error.html页面,就直接渲染
2.2 源码分析
发生错误以后,转发给/error路径,SpringBoot在底层写好一个 BasicErrorController的组件,专门处理这个请求。通过内容协商判断调用返回页面(errorHtml方法)
还是json或xml格式(error方法)
数据。
BasicErrorController.java
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
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
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);
}
分析返回页面
//1、解析错误的自定义视图地址
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
//2、如果解析不到错误页面的地址,默认的错误页就是 error
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
解析自定义视图 AbstractErrorController.java
protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status,
Map<String, Object> model) {
for (ErrorViewResolver resolver : this.errorViewResolvers) {
ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
if (modelAndView != null) {
return modelAndView;
}
}
return null;
}
通过容器的默认视图解析器解析错误页面DefaultErrorViewResolver.java
流程参考图示
@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) {
String errorViewName = "error/" + viewName;
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
this.applicationContext);
if (provider != null) {
return new ModelAndView(errorViewName, model);
}
return resolveResource(errorViewName, model);
}
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
for (String location : this.resources.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;
}
最佳实践
- 前后分离
后台发生的所有错误,@ControllerAdvice + @ExceptionHandler
进行统一异常处理。 - 服务端页面渲染
- 不可预知的一些,HTTP码表示的服务器或客户端错误
- 给
classpath:/templates/error/
下面,放常用精确的错误码页面。500.html
,404.html
- 给
classpath:/templates/error/
下面,放通用模糊匹配的错误码页面。5xx.html
,4xx.html
- 给
- 发生业务错误
- 核心业务,每一种错误,都应该代码控制,跳转到自己定制的错误页。
- 通用业务,
classpath:/templates/error.html
页面,显示错误信息。
- 不可预知的一些,HTTP码表示的服务器或客户端错误
参考
https://www.yuque.com/leifengyang/springboot3/wp5l9qbu1k64frz1#CLn90
---------------
我每一次回头,都感觉自己不够努力,所以我不再回头。
---------------