Spring Boot错误处理机制以及定制自己的错误页面

在我们使用Spring Boot的过程中肯定有遇到过发生错误的时候,这个时候可能页面上出现的就是Spring Boot默认的错误界面,有的时候我们希望能显示我们自己设置的错误界面,或者携带我们自己的错误响应参数,比如查询用户是否存在的时候,如果用户不存在,控制器可以选择抛出一个用户不存在异常,这个时候我们希望错误响应参数可以携带一个message响应参数表示用户不存在,而不是Spring Boot为我们配置好的那几个参数,这个时候我们应该这么做呢,这就是本文的目的。

如果要配置自己的错误界面,首先我们需要先了解一下Spring Boot是如何处理错误的。Spring Boot在自动配置类ErrorMvcAutoConfiguration中为我们添加了4个错误处理相关的组件,具体可以在IDEA中使用Ctrl+N来查找这个类,他们的作用可以查看下面代码中的注释:

1、DefaultErrorAttributes

//帮我们设置错误响应参数;
@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;
}

2、BasicErrorController

//处理/error请求,根据请求头中的信息(Accept参数)来判断优先返回html页面,还是JSON数据
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {

	@RequestMapping(produces = "text/html")//产生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);
		return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
	}
	
	@RequestMapping
	@ResponseBody //产生json数据,其他客户端来到这个方法处理;
	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);
	}
}

3、ErrorPageCustomizer

/**
配置错误发生后,到哪去。这里是系统出现错误以后来到error请求进行处理;
也就是到上面的BasicErrorController处理器,(相当于以前web.xml注册的错误页面规则)。
可以看到这个参数是可配置的。
**/
@Value("${error.path:/error}")
private String path = "/error"; 

4、DefaultErrorViewResolver

@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
		Map<String, Object> model) {
		
	ModelAndView modelAndView = resolve(String.valueOf(status), model);
	if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
		modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
	}
	return modelAndView;
}

private ModelAndView resolve(String viewName, Map<String, Object> model) {
	//默认SpringBoot可以去找到一个页面? 比如error/404
	String errorViewName = "error/" + viewName;
	
	//模板引擎可以解析这个页面地址就用模板引擎解析
	TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
		.getProvider(errorViewName, this.applicationContext);
		
	if (provider != null) {
		//模板引擎可用的情况下返回到errorViewName指定的视图地址
		return new ModelAndView(errorViewName, model);
	}
	
	//模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面error/404.html
	return resolveResource(errorViewName, model);
}

错误响应步骤如下:

一但系统出现4xx或者5xx之类的错误;ErrorPageCustomizer就会生效(定制错误的响应规则);就会来到/error请求;就会被BasicErrorController处理;如果决定产生html数据,而不是直接返回JSON数据,就使用DefaultErrorViewResolver试图解析器来解析我们的ModelAndView,最终决定返回哪个html文件。我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html,比如发生404错误,有404.html就不去4xx.html了);我们将这些错误响应的.html文件放到静态资源路径下的/error文件夹下即可。

通过上面的描述如果需要返回自己的错误返回界面,直接在静态资源文件夹下新建/error文件夹,将自己的错误响应.html文件放到里面即可,这些.html文件可能是4xx.html、404.html、5xx.html、500.html。具体响应哪个.html文件根据错误代码来决定。如果需要自定义错误响应信息,那么你还需要像下面这样做:

1、只响应JSON数据,而不管请求的是什么(比如请求.html页面,也返回JSON数据)

首先新建@ControllerAdvice类,而后在其中写@ExceptionHandler方法即可,如下:

@ControllerAdvice
public class MyExceptionHandler {
	@ResponseBody
	@ExceptionHandler(UserNotExistException.class)
	public Map<String,Object> handleException(Exception e){
		Map<String,Object> map = new HashMap<>();
		map.put("code","user.notexist");
		map.put("message",e.getMessage());
		return map;
	}
}

关于@ControllerAdvice注解更加详细的解释你可以看这里,在本文章中主要是结合@ExceptionHandler注解来处理错误。另外UserNotExistException是我们自定义的一个错误,由Controller抛出。

2、自适应响应

上面处理的方式有一个问题,就是不管是页面请求,还是JSON数据请求,我们都是返回JSON数据,如何做到请求JSON数据就返回JSON数据,请求页面就返回页面呢?我们可以回到上面说到的BasicErrorController组件,这个组件会自适应的处理/error请求,所以我们可以转发到/error界面,如下所示:

@ControllerAdvice
public class MyExceptionHandler {
	@ExceptionHandler(UserNotExistException.class)
	public String handleException(Exception e, HttpServletRequest request){
		//传入我们自己的错误状态码 4xx 5xx,否则就不会进入定制错误页面的解析流程
		request.setAttribute("javax.servlet.error.status_code",500);
		//转发到/error
		return "forward:/error";
	}
}

但是,这个时候我们又无法响应我们自己定制的错误响应数据了,我们希望不但可以自适应,又可以携带我们自定义的数据。如果你足够细心的话,可以发现,在上面BasicErrorController组件中,不论是响应HTML页面还是反回JSON数据,都会调用getErrorAttributes这个方法,来设置错误响应数据,这个方法来自容器中的DefaultErrorAttributes组件,这个组件会在我们没有在容器中添加ErrorAttributes组件的前提下添加到容器中作为默认的ErrorAttributes以返回错误响应数据;所以我们如果要响应我们自己的数据,我们就要写一个ErrorAttributes,并且实现它的getErrorAttributes方法,然后加入到容器中,为了简单起见我们直接,继承DefaultErrorAttributes然后重写getErrorAttributes方法即可:

//给容器中加入我们自己定义的ErrorAttributes
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
	@Override
	public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
				boolean includeStackTrace) {
		//保留默认的错误处理数据
		Map<String, Object> map = super.getErrorAttributes(requestAttributes
					,includeStackTrace);
		//由具体的错误处理器填入的数据,第二个参数的意思是scope,也就是从哪个域中获取,0:request,1:session, .....
		map.put("ext", RequestAttributes.getAttribute("ext", 0));
		//所有错误都会填入的错误响应数据
		map.put("company","BaiDu");
		return map;
	}
}

上面注释中说到的 “由具体的错误处理器填入的数据” 的意思是如下:

@ControllerAdvice
public class MyExceptionHandler {
	@ExceptionHandler(UserNotExistException.class)
	public String handleException(Exception e, HttpServletRequest request){
		//传入我们自己的错误状态码 4xx 5xx,否则就不会进入定制错误页面的解析流程
		request.setAttribute("javax.servlet.error.status_code",500);
		//填入额外的错误响应处理数据:
		Map<String,Object> map = new HashMap<>();
		map.put("code","user.notexist");
		map.put("message",e.getMessage());
		request.setAttribute("ext", map);
		//转发到/error
		return "forward:/error";
	}
}

这样一来我们就不但可以自适应响应数据,还可以定制自己的错误响应信息了。

总结:
上面详细讲述了Spring Boot中发生错误后,如何处理的的流程,并且自定义错误响应数据和界面。读者可以选择自己需要的功能来选择如何处理,主要是懂得原理,这些处理方式就能使用的游刃有余。简单分为以下两点:

  • 不需要自定义错误响应数据,只要到我们自己的错误响应界面,则编写错误代码对应的HTML文件,并将这些错误响应的.html文件放到静态资源路径下的/error文件夹下即可。
  • 需要自定义错误响应数据,并且自适应响应,则在容器中加入我们自己的ErrorAttributes组件即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值