SpringBoot web--错误处理(学习笔记19)

本文主要介绍在SpringBoot中如何进行web错误处理,包括全局异常捕获、自定义错误页面、错误信息定制等关键点,帮助开发者提升错误处理能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、比如说:

我先注释掉拦截器,让问题重现一下:



这里我访问一个不存在的请求路径,就出现了错误(There was an unexpected error (type=Not Found, status=404).)。通过客户端 Postman 模拟请求一下(下图所示),默认响应一个  JSON 数据:


原理:

    可以参照 ErrorMvcAutoConfiguration 进行错误处理的自动配置。

    给容器中添加了以下组件:

    1、DefaultErrorAttributes

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

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

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

    下面两个方法:第一个是产生 html 类型的数据(浏览器请求),第二个是产生 JSON 数据(客户端请求)

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

	@RequestMapping
	@ResponseBody
	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

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

    4、DefaultErrorViewResolver

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

    规则说明:

        如果系统出现 4xx 或者 5xx 之类的错误,ErrorPageCustomizer 就会生效(定制错误的响应规则)

        @Override
	public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
		ErrorPage errorPage = new ErrorPage(
				this.properties.getServlet().getServletPrefix()
						 this.properties.getError().getPath());
		errorPageRegistry.addErrorPages(errorPage);
	}

        通过 this.properties.getError().getPath(), 得到 path 信息:

        /**
	 * Path of the error controller.
	 */
	@Value("${error.path:/error}")
	private String path = "/error";

     就会来到/error请求; 就会被 BasicErrorController 处理(参考 BasicErrorController 下的两段代码),去哪个页面显示。去哪个页面是由 DefaultErrorViewResolver 解析的:

public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
	private static final Map<Series, String> SERIES_VIEWS;
	static {
		Map<Series, String> views = new EnumMap<>(Series.class);
		views.put(Series.CLIENT_ERROR, "4xx");
		views.put(Series.SERVER_ERROR, "5xx");
		SERIES_VIEWS = Collections.unmodifiableMap(views);
	}
        @Override
	public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
			Map<String, Object> model) {
		ModelAndView modelAndView = resolve(String.valueOf(status), 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) {//模板引擎可用的情况下返回到errorViewName指定的视图地址
			return new ModelAndView(errorViewName, model);
		}
		return resolveResource(errorViewName, model);//模板引擎不可用,就在静态资源文件夹找errorViewName对应的页面 error/404.html,如果没有返回为null
	}

2、如何定制错误响应

    2.1:如果是浏览器访问不存在请求路径,跳转错误页面,错误页面是定制的,要和整体网站的风格一致。

        2.1.1、有模板引擎的情况下:根据 error/状态码,将错误页面命名为:错误状态.html(404.html、4xx.html);把它们放在模板引擎文件夹里面的 error 文件夹下,发生相应的状态的错误就会找到对应的页面。

                    我们可以使用 4xx.html 和 5xx.html 页面来匹配相应状态码所有的错误(如果状态码没有匹配到具体的错误页面,就会指向此状态码统一页面。精确优先)

        2.1.2、没有模板引擎:模板引擎找不到错误页面,静态资源文件夹下存在

        2.1.3、模板引擎下没有、静态资源下也没有:就会使用默认的页面

        @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
		public BeanNameViewResolver beanNameViewResolver() {
			BeanNameViewResolver resolver = new BeanNameViewResolver();
			resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
			return resolver;
		}

	}

    2.2:如果是客户端请求,则要统一数据,定制返回数据

   

3、定制错误页面、返回定制错误json数据,并把定制数据进行显示

1、完全可以编写一个ErrorController的实现类 或者 编写AbstractErrorController的子类,放在容器中

2、页面上能用的数据,或者是json返回能用的数据都是通过 errorAttributes.getErrorAttributes 得到

感谢--尚硅谷


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值