本文最后更新于 2023-09-22,文章内容可能已经过时。

10 SpringBoot3【⑩ 错误处理】

10.1. 默认机制

错误处理的自动配置都在ErrorMvcAutoConfiguration中,两大核心机制:

  1. SpringBoot自适应 处理错误响应页面JSON数据
  2. SpringMVC的错误处理机制 依然保留,MVC处理不了,才会 交给boot进行处理

@ExceptionHandler注解作用在一个Controller方法,代表能处理当前Controller类内部的错误,而为了能处理全局的错误,SpringMVC中我们使用单独定义一个类在handler包下,并标记@ControllerAdvice注解,此时这个Handler类内部的方法再去使用 @ExceptionHandler注解处理的就是全局的错误

错误处理机制的底层讲解部分视频
1

  • 发生错误以后,转发给/error路径,SpringBoot在底层写好一个 BasicErrorController的组件,专门处理这个请求
	@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  //返回 ResponseEntity, 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);
	}

错误页面是这么解析到的

//1、解析错误的自定义视图地址
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
//2、如果解析不到错误页面的地址,默认的错误页就是 error
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);

容器中专门有一个错误视图解析器

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

SpringBoot解析自定义错误页的默认规则

	@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;
	}

容器中有一个默认的名为 error 的 view; 提供了默认白页功能

@Bean(name = "error")
@ConditionalOnMissingBean(name = "error")
public View defaultErrorView() {
    return this.defaultErrorView;
}

封装了JSON格式的错误信息

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

规则:

  1. 解析一个错误页

    • 如果发生了500、404、503、403 这些错误

      • ① 如果 有模板引擎,默认在 classpath:/templates/error/精确码.html
      • ② 如果 没有模板引擎,在 静态资源文件夹 下找 精确码.html
    • 如果匹配不到 精确码.html 这些精确的错误页,就去找 5xx.html4xx.html模糊匹配

      • ① 如果有模板引擎,默认在 classpath:/templates/error/5xx.html(或者4xx.html
      • ② 如果没有模板引擎,在静态资源文件夹下找 5xx.html (或者4xx.html
  2. 如果模板引擎路径templates下有 error.html页面,就直接渲染

  3. 即便是以上全没有,容器中有一个默认的名为 error 的 view,并且底层配置了渲染的内容,提供了默认白页功能

10.2. 自定义错误响应

10.2.1. 自定义json响应

使用@ControllerAdvice + @ExceptionHandler 进行统一异常处理
@RestControllerAdvice + @ExceptionHandler + 自定义返回工具类 是最优实战

10.2.2. 自定义页面响应

根据boot的错误页面规则,自定义页面模板

10.3. 最佳实战

  • 前后分离
    • 后台发生的所有错误,@ControllerAdvice + @ExceptionHandler进行统一异常处理。
  • 服务端页面渲染
    • 不可预知的一些,HTTP码表示的服务器或客户端错误
      • classpath:/templates/error/ 下面,放常用精确的错误码页面。500.html404.html
      • classpath:/templates/error/ 下面,放通用模糊匹配的错误码页面。 5xx.html4xx.html
    • 发生业务错误
      • 核心业务,每一种错误,都应该代码控制,跳转到自己定制的错误页(thy catch代码块,出现异常跳转定制页面)。
      • 通用业务classpath:/templates/error.html页面,显示错误信息。

页面,JSON,可用的Model数据如下,如果使用Thymeleaf,可以直接从作用域取这些对象展示。
2