spring boot入门指南-07错误处理

1. 前言

在Spring boot无论是后端错误还是前端页面的错误,任何的错误页面都有相应的对应。在Spring boot中是如何控制错误页面的样式的呢?

2. 源码分析

首先看与错误处理有关的自动配置类ErrorMvcAutoConfiguration

该配置工作之前有两个前提条件:

  1. 该应用必须是一个servlet-web的应用
  2. Servlet和DispatcherServlet必须存在于classpath
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })

继续看源码发现,该配置类给容器中注入了四个组件分别是

  1. DefaultErrorAttributes
  2. BasicErrorController
  3. ErrorPageCustomizer
  4. PreserveErrorControllerTargetClassPostProcessor(目的是为ErrorController bean的定义设置属性preserveTargetClass:true,从而确保ErrorController bean如果有代理对象的话代理对象总是代理在类上而不是接口上。)

1. 核心配置类分析

一个静态的内部注解类
DefaultErrorViewResolverConfiguration
该配置类源码,该配置类的内部其实是由DefaultErrorViewResolver注入进去的。

@Configuration
static class DefaultErrorViewResolverConfiguration {

	private final ApplicationContext applicationContext;

	private final ResourceProperties resourceProperties;

	DefaultErrorViewResolverConfiguration(ApplicationContext applicationContext,
			ResourceProperties resourceProperties) {
		this.applicationContext = applicationContext;
		this.resourceProperties = resourceProperties;
	}

	注入的前提是:1. DispatcherServlet存在,并且DefaultErrorViewResolver不存在。
	@Bean
	@ConditionalOnBean(DispatcherServlet.class)
	@ConditionalOnMissingBean
	public DefaultErrorViewResolver conventionErrorViewResolver() {
		return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
	}

}

DefaultErrorViewResolver为一个默认的错误视图解析类,作用是决定去哪儿映射对应的页面。
其内部主要的方法的思路如下:

  1. 请求交由resolveErrorView寻找错误视图。
  2. 真正的页面处理动作由下面的resolve执行,具体是根据错误码去寻找对应的页面。
  3. 比如:如果返回错误码为404,则映射error/404,如果提供了模板引擎可以解析这个页面地址就用模板引擎解析;如果模板引擎不可用,则静态资源文件夹下找errorViewName对应的页面 error/404.html
  4. 在引擎不可用的时候,用resolveResource来寻找页面。
@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;
}


//该方法真正的视图映射方法。具体是根据错误码去寻找对应的页面。
//比如:如果返回错误码为404,则映射error/404
//如果提供了模板引擎可以解析这个页面地址就用模板引擎解析
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);
	}
	//如果模板引擎不可用,则静态资源文件夹下找errorViewName对应的页面   error/404.html
	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;
}

2. 组件分析

1. DefaultErrorAttributes

核心思路:

  1. 当用户没有自己配置Err@orAttributes时候,DefaultErrorAttributes才会生效。
  2. DefaultErrorAttributes通过getErrorAttributes方法将对一个的属性添加进去。
@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
	return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
}

//添加对一个的错误属性
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
	Map<String, Object> errorAttributes = new LinkedHashMap<>();
	errorAttributes.put("timestamp", new Date());
	addStatus(errorAttributes, webRequest);
	addErrorDetails(errorAttributes, webRequest, includeStackTrace);
	addPath(errorAttributes, webRequest);
	return errorAttributes;
}

主要的错误信息

<ul>
<li>timestamp - The time that the errors were extracted</li>
<li>status - The status code</li>
<li>error - The error reason</li>
<li>exception - The class name of the root exception (if configured)</li>
<li>message - The exception message</li>
<li>errors - Any {@link ObjectError}s from a {@link BindingResult} exception
<li>trace - The exception stack trace</li>
<li>path - The URL path when the exception was raised</li>
</ul>

2. BasicErrorController

该组件的核心作用如下:

  1. 当用户自己没有配置ErrorController时,BasicErrorController才会生效。
  2. controller针对于浏览器和json格式的请求做不同样式的处理
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
	return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);
}

(1):产生html类型的数据;浏览器发送的请求来到这个方法处理

@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, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
	response.setStatus(status.value());
	ModelAndView modelAndView = resolveErrorView(request, response, status, model);
	return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}

(2):产生json数据,其他客户端来到这个方法处理;

@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
	Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
	HttpStatus status = getStatus(request);
	return new ResponseEntity<>(body, status);
}

3. ErrorPageCustomizer

配置错误页面,同样由内部类ErrorPageCustomizer实现,该类实现了接口ErrorPageRegistrar,该接口实际上是当前Servlet容器对象的的工厂。
系统出现错误以后来到error请求进行处理;

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

ErrorPageCustomizer的核心方法,为注册器提供一个错误页面获取方法,获取对应的controller的访问路径

@Override
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
	ErrorPage errorPage = new ErrorPage(
			this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
	errorPageRegistry.addErrorPages(errorPage);
}
@Value("${error.path:/error}")
private String path = "/error";

3. 如何自定义错误响应

1. 自定义错误页面

1)、如何定制错误的页面;

1)、有模板引擎的情况下;error/状态码;
【将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的 error文件夹下】,发生此状态码的错误就会来到 对应的页面;

​我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html);

页面能获取的信息;
timestamp:时间戳
status:状态码
error:错误提示
exception:异常对象
message:异常消息
errors:JSR303数据校验的错误都在这里

2)、没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找;
3)、以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值