springboot-web开发错误自动配置原理、自定义错误页面和JSON数据

一、错误自动处理原理

springboot的错误处理自动配置类是ErrorMvcAutoConfiguration,我们请求发生错误时,如果是浏览器请求则返回html页面,如果是客户端请求则返回JSON数据,下面我们来看看是如何处理,类ErrorMvcAutoConfiguration代码如下

public class ErrorMvcAutoConfiguration {
	@Bean
	@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
	public DefaultErrorAttributes errorAttributes() {
		return new DefaultErrorAttributes();
	}
	@Bean
	@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
	public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
		return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
				this.errorViewResolvers);
	}
	@Bean
	public ErrorPageCustomizer errorPageCustomizer() {
		return new ErrorPageCustomizer(this.serverProperties);
	}
	@Configuration
	static class DefaultErrorViewResolverConfiguration {
		@Bean
		@ConditionalOnBean(DispatcherServlet.class)
		@ConditionalOnMissingBean
		public DefaultErrorViewResolver conventionErrorViewResolver() {
			return new DefaultErrorViewResolver(this.applicationContext,
					this.resourceProperties);
		}
	}

这里给容器创建了四个重要的错误处理bean,DefaultErrorAttributes,BasicErrorController,ErrorPageCustomizer,DefaultErrorViewResolver。

方法getErrorAttributes返回的Map里包含了错误请求返回的数据,包括时间戳,状态码,错误消息,异常信息等,如果我们想自定义自己的错误信息就可以重写这个方法
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DefaultErrorAttributes
		implements ErrorAttributes, HandlerExceptionResolver, Ordered {
	@Override
	public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
			boolean includeStackTrace) {
		Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
		errorAttributes.put("timestamp", new Date());
		addStatus(errorAttributes, requestAttributes);
		addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
		addPath(errorAttributes, requestAttributes);
		return errorAttributes;
	},
默认处理/error请求,浏览器和客户端请求的区别就是看request请求头header里有没有accept对象,如果有就表示是浏览器请求
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
    
    浏览器请求不仅要返回错误信息还要返回html
	@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);
        如果不存在返回的视图就返回默认的error视图
		return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
	}
    客户端请求返回JSON数据
	@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<Map<String, Object>>(body, status);
	}


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>");
        
        ErrorMvcAutoConfiguration这里定义了一个视图类bean,名称为error,就是返回的默认视图
		@Bean(name = "error")
		@ConditionalOnMissingBean(name = "error")
		public View defaultErrorView() {
			return this.defaultErrorView;
		}

 

这个类是寻找错误页面,默认到/error请求,如果没有配置页面路径的话,默认到类路径下error目录下寻找,通过状态码进行匹配获取
private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {

		@Override
		public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
			ErrorPage errorPage = new ErrorPage(this.properties.getServletPrefix()
                    方法getPath就是寻找error目录下的错误页面
					+ this.properties.getError().getPath());
			errorPageRegistry.addErrorPages(errorPage);
		}
	}
@Value("${error.path:/error}")
private String path = "/error";

public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
	static {
		Map<Series, String> views = new HashMap<Series, String>();
        添加状态码寻找界面时进行匹配
		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) {
        通过状态码去寻找html
		ModelAndView modelAndView = resolve(String.valueOf(status), model);
        html不存在的话则使用通配符4XX或5XX进行匹配
		if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
			modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
		}
		return modelAndView;
	}
    直接到error目录下寻找页面,如果存在的话直接返回视图
	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) {
        error目录下不存在匹配的界面就到静态资源static下去寻找html
		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;
	}

二、自定义错误返回页面和JSON数据

1.自定义返回界面的话非常简单,只需要在类路径的templates下建立error文件夹,里面放对应的错误页面即可,或者可以使用一个通用的错误页面,4xx.html,也可以在静态资源目录static下建立error文件夹,错误处理类都可以找得到

 2.自定义错误信息

@Component
public class MyErrorAttributes extends DefaultErrorAttributes{
	
	@Override
	public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
		Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
		map.put("name", requestAttributes.getAttribute("name", 0));
		return map;
	}
}

我们需要自定义JSON数据只需要继承DefaultErrorAttributes重写获取错误信息方法就行了,可以放入自定义的信息内容

3.自适应错误返回页面还是JSON数据

我们在上面已经解释过默认请求的处理是/error,它会自己去判断当前请求是浏览器发送还是客户端发送,所以我们的异常处理只需要将错误信息转发到/error请求,就可以自适应返回页面还是JSON了

@ControllerAdvice 声明这个类是异常处理类必须使用@ControllerAdvice
public class MyExceptionHandler {
	
	@ExceptionHandler(MyException.class)声明方法处理哪个异常使用注解@ExceptionHandler
    public String handleException(Exception e, HttpServletRequest request){
        //传入我们自己的错误状态码  4xx 5xx
        /**
         * Integer statusCode = (Integer) request
         .getAttribute("javax.servlet.error.status_code");
         */
        request.setAttribute("javax.servlet.error.status_code",500);

        request.setAttribute("name","芜湖最靓的仔");
        //转发到/error
        return "forward:/error";
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值