SpringBoot异常自动处理机制

SpringBoot异常自动处理机制

ErrorMvcAutoConfiguration中注入了四个重要的类,分别是

DefaultErrorAttributes(用处共享页面中的数据)、BasicErrorController、ErrorPageCustomizer、DefaultErrorViewResolver四个类。

当发生错误时,

1、ErrorPageCustomer会将请求转发到/error地址中,

2、然后通过BasicErrorController来处理,最终的处理结果是返回一个ModelAndView(指定了转发的路径和需要渲染的数据)

​ 这里SpringBoot是通过请求头中的信息来辨别是浏览器还是客户端

  • 如果是浏览器请求则请求BasicErrorController.errorHtml方法,响应的是一个ModelAndView。

  • 如果是客户端请求则请求BasicErrorController.error方法,响应的是JSON形式的数据

    (这里以浏览器请求为例子)

2.1、调用BasicErrorController.errorHtml(HttpServletRequest request,HttpServletResponse response)方法

  @RequestMapping(produces = "text/html")
  public ModelAndView errorHtml(HttpServletRequest request,
      HttpServletResponse response) {
    HttpStatus status = getStatus(request);
    // 此处调用了getErrorAttributes来得到model,这里的getErrorAttributes就是之前注入的四个Bean之一,DefaultErrorAttributes中的方法。此方法中可以获取状态码、时间戳、错误提示等
    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 ? new ModelAndView("error", model) : modelAndView);
  }

2.2、errorHtml方法中调用了AbstractErrorController.resolveErrorView(HttpServletRequest request,HttpServletResponse response, HttpStatus status, Map<String, Object> model)方法。

	// AbstractErrorController.resolveErrorView方法
	protected ModelAndView resolveErrorView(HttpServletRequest request,
			HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
    // (注意:这里的ErrorMvcAutoConfiguration.errorViewResolvers和AbstractErrorController.errorViewResolvers是同一个,是ErrorMvcAutoConfiguration在注入Bean的时候通过构造方法传入的)
		for (ErrorViewResolver resolver : this.errorViewResolvers) {
      // 遍历所有的视图解析器,找到适合的解析器
      // 这里调用了DefaultErrorViewResolver.resolveErrorView方法
			ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
			if (modelAndView != null) {
				return modelAndView;
			}
		}
    // 如果所有的视图解析器都无法解析则返回null
		return null;
	}

2.3、在遍历所有视图解析器的时候调用了DefaultErrorViewResolver.resolveErrorView方法

	// DefaultErrorViewResolver.resolveErrorView
	@Override
	public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
			Map<String, Object> model) {
    // 首先传入具体的状态码来解析,如404
		ModelAndView modelAndView = resolve(String.valueOf(status), model);
    
    // 如果视图为空,并且错误代码为4xx或者5xx则传入状态码和model进行解析
		if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
			modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
		}
		return modelAndView;
	}

 	// DefaultErrorViewResolver.resolve方法
	private ModelAndView resolve(String viewName, Map<String, Object> model) {
    // 根据传入的状态码去 "error/" 下寻找是否有对应页面
		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);
	}

	// 去静态资源文件夹下找对应的页面,找不到则返回null
	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.4、注意看2.1步骤中的代码,如果最后解析返回一个ModelAndView则会直接渲染后响应给请求,如果返回的是null,则会生成一个名为 “error” 的 ModelAndView ,也就是我们熟知的默认错误页面。

	@Configuration
	@ConditionalOnProperty(prefix = "server.error.whitelabel", name = "enabled", matchIfMissing = true)
	@Conditional(ErrorTemplateMissingCondition.class)
	protected static class WhitelabelErrorViewConfiguration {

		private final SpelView defaultErrorView = new SpelView(
				"<html><body><h1>Whitelabel Error Page</h1>"
						+ "<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>"
						+ "<div id='created'>${timestamp}</div>"
						+ "<div>There was an unexpected error (type=${error}, status=${status}).</div>"
						+ "<div>${message}</div></body></html>");

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

		// If the user adds @EnableWebMvc then the bean name view resolver from
		// WebMvcAutoConfiguration disappears, so add it back in to avoid disappointment.
		@Bean
		@ConditionalOnMissingBean(BeanNameViewResolver.class)
		public BeanNameViewResolver beanNameViewResolver() {
			BeanNameViewResolver resolver = new BeanNameViewResolver();
			resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
			return resolver;
		}

	}

如何定制错误的响应信息

定制错误页面

在上面也2.3中有分析到DefaultErrorViewResolver中视图解析器是如何解析的,**有模板引擎的情况下直接将对应错误状态码的html页面放入error文件夹下即可。**也可以使用 “4xx.html” 或者"5xx.html" 来处理一种类型的错误。没有模板引擎则直接去静态资源文件夹下找,如果静态资源也找不到则返回SpringBoot的默认错误页面

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值