Spring boot Web MVC : 缺省全局错误控制器 BasicErrorController

介绍Spring Boot中的BasicErrorController,它是Spring Boot默认的全局错误控制器,用于处理应用中的HTTP错误,并支持返回HTML和JSON格式的错误信息。

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

BasicErrorControllerSpring boot提供的缺省全局错误控制器Controller。该控制器提供了两个控制器方法,这两个方法逻辑类似,都是处理请求上的错误属性,使用缺省的错误解析机制处理错误属性返回结果给客户端,但分别对应返回HTML格式和JSON格式。

这里缺省的错误处理机制是指 :

  1. 如果使用DefaultErrorViewResolver可以找到匹配特定HTTP错误状态码的错误视图,则使用这些错误视图处理错误属性并返回客户端;
    • 这里DefaultErrorViewResolver会根据错误码从以下常用位置按如下顺序寻找错误视图(例子假定HTTP状态码是404) :
      • /<templates>/error/404.<ext>
      • /<static>/error/404.html
  2. 否则使用名称为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;
	}

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值