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 getErrorAttributes(RequestAttributes requestAttributes,
			boolean includeStackTrace) {
		Map errorAttributes = new LinkedHashMap();
		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 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> error(HttpServletRequest request) {
		Map body = getErrorAttributes(request,
				isIncludeStackTrace(request, MediaType.ALL));
		HttpStatus status = getStatus(request);
		return new ResponseEntity>(body, status);
	}


private final SpelView defaultErrorView = new SpelView(
				"

Whitelabel Error Page

" + "

This application has no explicit mapping for /error, so you are seeing this as a fallback.

" + "
${timestamp}
" + "
There was an unexpected error (type=${error}, status=${status}).
" + "
${message}
"); 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 views = new HashMap();
        添加状态码寻找界面时进行匹配
		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 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 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 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文件夹,错误处理类都可以找得到

springboot-web开发错误自动配置原理、自定义错误页面和JSON数据_第1张图片

 2.自定义错误信息

@Component
public class MyErrorAttributes extends DefaultErrorAttributes{
	
	@Override
	public Map getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
		Map 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";
    }
}

 

你可能感兴趣的:(SpringBoot)