SpringBoot(十八)异常统一处理及异常处理解析

目录

默认异常处理

BasicErrorController

返回页面+数据:errorHtml()

自定义统一异常处理 

静态异常处理页面

动态异常处理(模板引擎)页面

自定义异常数据 


SpringBoot版本:2.1.1 

默认情况下,Spring Boot异常页面显示如下: 

SpringBoot(十八)异常统一处理及异常处理解析_第1张图片

 下面第一句:此应用程序没有针对/error的显式映射。

默认异常处理

Spring Boot提供了一个处理所有异常的映射 “ /error ”,并将其注册为容器中的“全局”异常页面,位于org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController

对于计算机客户端(比如postman),它会生成一个JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。

SpringBoot(十八)异常统一处理及异常处理解析_第2张图片

对于浏览器客户端,有一个默认的异常视图,它以HTML格式呈现相同的数据,就是上面的默认异常页面。

BasicErrorController

可以看到默认处理就是“/error”请求。有两个请求映射方法errorHtml()和error(),第一个方法是返回页面+数据,第二个方法是放回JSON。

返回页面+数据:errorHtml()

最终是调用DefaultErrorViewResolver的resolveErrorView方法创建ModelAndView 对象,如果返回的ModelAndView 为空,则显示的就是默认的异常页面。

@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request,
		HttpServletResponse response) {
        //request.getAttribute("javax.servlet.error.status_code");如果为空则返回500
	HttpStatus status = getStatus(request);
        //得到异常信息,包括timestamp,status,error,message,path;
        //具体可以到DefaultErrorAttributes的getErrorAttributes方法查看
	Map model = Collections.unmodifiableMap(getErrorAttributes(
			request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        //设置响应状态
	response.setStatus(status.value());
        //创建ModelAndView 
	ModelAndView modelAndView = resolveErrorView(request, response, status, model);
	return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}

SpringBoot(十八)异常统一处理及异常处理解析_第3张图片

 看下DefaultErrorViewResolver的类注释。

SpringBoot(十八)异常统一处理及异常处理解析_第4张图片

大概意思如下: 

ErrorViewResolver的默认实现,将使用状态代码和状态序列在“/error”目录下搜索模板和静态资源。

例如,HTTP 404将搜索(按特定顺序):

  • //error/404.
  • //error/404.html
  • //error/4xx.
  • //error/404.html

首先以异常响应码作为视图名分别去查找错误模板和静态页面,如果为空,再以 4xx 或者 5xx 作为视图名再去分别查找错误模板或者静态页面。

Spring Boot支持的模板有:

  • FreeMarker
  • Groovy
  • Thymeleaf
  • Mustache

看下面的resolveErrorView()方法,逻辑跟上面注释说的顺序是一样的。

@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
		Map model) {
        //传入状态码和错误信息,这里是500
        //第一次根据状态码先找模板,如果没找到,再找HTML页面
	ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
	if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
                //第二次根据4xx或5xx找,同样先找模板,在找HTML页面
		modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
	}
	return modelAndView;
}

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) {
        //再找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;
}

静态资源的四个默认路径,模板的默认路径是在org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties定义的,即默认的前缀后缀,有配置项可以更改。 

SpringBoot(十八)异常统一处理及异常处理解析_第5张图片

SpringBoot(十八)异常统一处理及异常处理解析_第6张图片

自定义统一异常处理 

静态异常处理页面

知道了Spring Boot异常处理的逻辑,现在可以自定义异常页面了。分为两种,第一种 是使用 HTTP 响应码来命名页面,例如 404.html、405.html、500.html ....,另一种就是直接定义一个 4xx.html,表示400-499 的状态都显示这个异常页面,5xx.html 表示 500-599 的状态显示这个异常页面。

在resources下新建static目录,再建一层error目录,然后新建404.html和5xx.html。一样的建一个演示下。http状态码自行去了解吧。

两个HTML的内容就不展示,就在下面,简单的输出。

 SpringBoot(十八)异常统一处理及异常处理解析_第7张图片

效果如下:

SpringBoot(十八)异常统一处理及异常处理解析_第8张图片

SpringBoot(十八)异常统一处理及异常处理解析_第9张图片

动态异常处理(模板引擎)页面

我用的是thymelef,导入依赖:


    org.springframework.boot
    spring-boot-starter-thymeleaf

添加配置项:spring.thymelef.cache=false //开发时关闭缓存,不然没法看到实时页面

在resources下新建templates目录,同样再建一层error目录,然后新建4xx.html和5xx.html。一样的建一个演示下。

SpringBoot(十八)异常统一处理及异常处理解析_第10张图片

4xx.html文件内容,5xx.html内容一样,只是h1里面内容不一样:





Insert title here


	

4xx templates

path
error
message
timestamp
status

效果如下:

SpringBoot(十八)异常统一处理及异常处理解析_第11张图片

SpringBoot(十八)异常统一处理及异常处理解析_第12张图片

自定义异常数据 

model中的异常数据是在org.springframework.boot.web.servlet.error.DefaultErrorAttributes的getErrorAttributes()方法中定义的,方便点,你可以继承DefaultErrorAttributes,重写该方法,异常数据在父类已经定义好了,只要拿来用。

如下:

import java.util.Map;

import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;
@Component
public class CustomErrorAttributes extends DefaultErrorAttributes {
	@Override
	public Map getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
		Map errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);
		if ((int)errorAttributes.get("status")==500) {
			errorAttributes.replace("message", "服务器内部错误");
		}
		return errorAttributes;
	}
}

SpringBoot(十八)异常统一处理及异常处理解析_第13张图片

当然还有其它方法,ErrorPageRegistrar接口,添加自定义的ErrorPage;@ControllerAdvice注解配合@ExceptionHandler(value=Exception.class)注解。

个人觉得上面几种方法已经能满足基本需要了。

你可能感兴趣的:(springboot,SpringBoot学习记录)