BasicErrorController
是Spring boot
提供的缺省全局错误控制器Controller
。该控制器提供了两个控制器方法,这两个方法逻辑类似,都是处理请求上的错误属性,使用缺省的错误解析机制处理错误属性返回结果给客户端,但分别对应返回HTML
格式和JSON
格式。
这里缺省的错误处理机制是指 :
- 如果使用
DefaultErrorViewResolver
可以找到匹配特定HTTP
错误状态码的错误视图,则使用这些错误视图处理错误属性并返回客户端;- 这里
DefaultErrorViewResolver
会根据错误码从以下常用位置按如下顺序寻找错误视图(例子假定HTTP
状态码是404) :/<templates>/error/404.<ext>
/<static>/error/404.html
- 这里
- 否则使用名称为
error
的错误视图处理错误属性并返回给客户端;- 缺省情况下,
error
对应的视图其实就是常见的Whitelabel Error Page
。
- 缺省情况下,
更特殊一些的错误处理可以使用其他一些Spring MVC
抽象进行(比如通过@ExceptionHandler
)或者通过增加Servlet
错误页面(AbstractServletWebServerFactory#setErrorPages
)。
注意该类上的注解@RequestMapping("${server.error.path:${error.path:/error}}")
,该注解会将该Controller
映射到配置参数${server.error.path:${error.path:/error}}
对应的路径上:
- 如果开发人员提供了参数
server.error.path
则使用该参数值作为映射路径; - 否则如果开发人员提供了参数
error.path
则使用该参数值作为映射路径; - 否则使用缺省映射路径
/error
。
源代码解析
1. BasicErrorController
package org.springframework.boot.autoconfigure.web.servlet.error;
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
private final ErrorProperties errorProperties;
/**
* Create a new BasicErrorController instance.
* @param errorAttributes the error attributes 从请求中获取错误属性的工具,缺省是一个
* DefaultErrorAttributes 对象
* @param errorProperties configuration properties 错误属性配置,对应配置参数项 server.error.*
*/
public BasicErrorController(ErrorAttributes errorAttributes,
ErrorProperties errorProperties) {
this(errorAttributes, errorProperties, Collections.emptyList());
}
/**
* Create a new BasicErrorController instance.
* @param errorAttributes the error attributes 从请求中获取错误属性的工具,缺省是一个
* DefaultErrorAttributes 对象
* @param errorProperties configuration properties 错误属性配置,对应配置参数项 server.error.*
* @param errorViewResolvers error view resolvers 错误视图解析器,缺省会含有一个
* DefaultErrorViewResolver 实例
*/
public BasicErrorController(ErrorAttributes errorAttributes,
ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, errorViewResolvers);
Assert.notNull(errorProperties, "ErrorProperties must not be null");
this.errorProperties = errorProperties;
}
@Override
public String getErrorPath() {
return this.errorProperties.getPath();
}
// Web控制器方法 : 向客户端返回HTML格式的错误信息
// 这里的@RequestMapping注解会结合当前类上的@RequestMapping注解一起使用
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
// 从请求属性中获取HTTP状态码
HttpStatus status = getStatus(request);
// 获取和组织最终要展示的错误信息
// 方法 getErrorAttributes 实现在基类 AbstractErrorController 中
Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
// 首先尝试根据状态码解析是否有开发人员提供的相应的错误展示页面
// /<templates>/error/404.<ext>, /<static>/static/404.html 等等
// 该方法实现在基类 AbstractErrorController 中
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
// 如果开发人员没有指定针对特定HTTP状态码的错误展示页面,则使用缺省的错误处理页面,对应
// 视图名称为 error, 缺省情况也就是Spring MVC中常说的 Whitelabel Error Page
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
// Web控制器方法 : 向客户端返回JSON格式的错误信息
// 这里的@RequestMapping注解会结合当前类上的@RequestMapping注解一起使用
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
// 获取和组织最终要展示的错误信息
// 方法 getErrorAttributes 实现在基类 AbstractErrorController 中
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
// 从请求属性中获取HTTP状态码
HttpStatus status = getStatus(request);
return new ResponseEntity<>(body, status);
}
/**
* Determine if the stacktrace attribute should be included.
* @param request the source request
* @param produces the media type produced (or MediaType.ALL}
* @return if the stacktrace attribute should be included
*/
protected boolean isIncludeStackTrace(HttpServletRequest request,
MediaType produces) {
IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();
if (include == IncludeStacktrace.ALWAYS) {
return true;
}
if (include == IncludeStacktrace.ON_TRACE_PARAM) {
return getTraceParameter(request);
}
return false;
}
/**
* Provide access to the error properties.
* @return the error properties
*/
protected ErrorProperties getErrorProperties() {
return this.errorProperties;
}
}
2. 基类AbstractErrorController
由上面BasicErrorController
的代码可见,其中部分调用的方法有基类提供。该基类AbstractErrorController
代码如下 :
package org.springframework.boot.autoconfigure.web.servlet.error;
public abstract class AbstractErrorController implements ErrorController {
private final ErrorAttributes errorAttributes;
private final List<ErrorViewResolver> errorViewResolvers;
public AbstractErrorController(ErrorAttributes errorAttributes) {
this(errorAttributes, null);
}
public AbstractErrorController(ErrorAttributes errorAttributes,
List<ErrorViewResolver> errorViewResolvers) {
Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
this.errorAttributes = errorAttributes;
this.errorViewResolvers = sortErrorViewResolvers(errorViewResolvers);
}
private List<ErrorViewResolver> sortErrorViewResolvers(
List<ErrorViewResolver> resolvers) {
List<ErrorViewResolver> sorted = new ArrayList<>();
if (resolvers != null) {
sorted.addAll(resolvers);
AnnotationAwareOrderComparator.sortIfNecessary(sorted);
}
return sorted;
}
// 从请求对象上获取错误属性
protected Map<String, Object> getErrorAttributes(HttpServletRequest request,
boolean includeStackTrace) {
WebRequest webRequest = new ServletWebRequest(request);
return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
}
protected boolean getTraceParameter(HttpServletRequest request) {
String parameter = request.getParameter("trace");
if (parameter == null) {
return false;
}
return !"false".equalsIgnoreCase(parameter);
}
// 从请求独享属性中获取处理过程中所设置的HTTP错误状态码,属性名称使用 javax.servlet.error.status_code,
// 如果该属性未设置或者转换HttpStatus失败则缺省返回 HttpStatus : 500 INTERNAL_SERVER_ERROR
protected HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request
.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
try {
return HttpStatus.valueOf(statusCode);
}
catch (Exception ex) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
}
/**
* Resolve any specific error views. By default this method delegates to
* ErrorViewResolver ErrorViewResolvers
* 根据HTTP状态码,请求对象,响应对象中的信息解析错误视图。该方法并不直接做视图解析,
* 而是将工作委托给设置给自己的一组 errorViewResolvers, 缺省情况下该 errorViewResolvers
* 只有一个元素,是一个 DefaultErrorViewResolver 对象 。
* @param request the request
* @param response the response
* @param status the HTTP status
* @param model the suggested model
* @return a specific ModelAndView or null if the default should be used。
* 如果该方法通过 errorViewResolvers 能解析到一个错误视图,则构造并返回一个ModelAndView
* 对象,否则则返回null,表示要使用缺省错误视图
*/
protected ModelAndView resolveErrorView(HttpServletRequest request,
HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
for (ErrorViewResolver resolver : this.errorViewResolvers) {
ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
if (modelAndView != null) {
return modelAndView;
}
}
return null;
}
}