SpringBoot|SpringBoot - Web开发 自定义错误页面处理
在Web开发中,SpringBoot会自动帮你配置了错误页面。并且在不同端请求,会有不同的效果。那么假如我们想自定义错误页面应该如何做配置呢?
文章图片
错误请求 一.错误页面配置原理
谈到原理,我们就要回到SpringBoot的自动配置类ErrorMvcAutoConfiguration去查看。
通过代码,我们看到SpringBoot配置了以下几个类:
- DefaultErrorAttributes
- BasicErrorController
- errorPageCustomizer
- DefaultErrorViewResolver
- 当出现错误时,errorPageCustomizer就会生效,并来到/error请求,由BasicErrorController来处理
//errorPageCustomizer
@Value("${error.path:/error}")
private String path = "/error";
BasicErrorController是一个Controller,其中里面有两种处理方法,一种是HTML形式,一种是JSON格式。其中我们的页面信息可以从getErrorAttributes从获取。DefaultErrorAttributes是ErrorAttributes的实现类。
@RequestMapping(produces = "text/html") //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);
return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
}@RequestMapping
@ResponseBody //JSON
public ResponseEntity
- 当为HTML模式时,就会构建一个resolveErrorView类,而resolverErrorView继续调用ErrorViewResolver。
protected ModelAndView resolveErrorView(HttpServletRequest request,
HttpServletResponse response, HttpStatus status, Map model) {
for (ErrorViewResolver resolver : this.errorViewResolvers) {
ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
if (modelAndView != null) {
return modelAndView;
}
}
return null;
}
3.在我们没有做自定义配置时,ErrorViewResolver就会指向DefaultErrorViewResolver。
static {
//可以用4xx,5xx文件名来统一匹配错误,但是会以精确优先的原则
Map views = new EnumMap<>(Series.class);
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) {
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 model) {
//将错误代码拼接到error后
String errorViewName = "error/" + viewName;
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
.getProvider(errorViewName, this.applicationContext);
//如果模版引擎可用就让模版引擎进行解析如:Template/error/404
if (provider != null) {
return new ModelAndView(errorViewName, model);
}
//如果模版引擎不可用,就在静态资源文件夹下找资源文件,error/404
return resolveResource(errorViewName, model);
}
4.自定义错误页面
- 查看DefaultErrorAttributes我们可以获取到的参数。
- timestamp - The time that the errors were extracted
- status - The status code
- error - The error reason
- exception - The class name of the root exception (if configured)
- message - The exception message
- errors - Any {@link ObjectError}s from a {@link BindingResult} exception
- trace - The exception stack trace
- path - The URL path when the exception was raised
status:[[${status}]]
timestamp:[[${timestamp}]]
二.自定义错误页面 根据上述原理,只要将我们自定义的错误页面按照错误码放入相应路径。优先会取模版引擎的错误界面(template/error/404,template/error/4xx),其次取静态资源文件夹下(static/error/404,template/error/4xx,public/error/404,template/error/4xx),如果前两者都没有,再取SpringBoot的默认页面。
三.自定义错误返回 自定义错误页面只针对TEXT_HTML的方式,要自定义类似于SpringBoot自动配置(网页访问返回错误页面,移动端请求返回错误JSON串)的返回我们应该如何做呢?
流程:
出现错误之后,先将页面转发到/error,它就会被BasicErrorController处理,而其中数据是由getErrorAttributes得到的。
所以我们的实现思路是:
- 编写一个ErrorController的实现类,放在容器中。
- 页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes得到;重写DefaultErrorAttributes 进行数据处理。
@Controller
public class TestController {
@ResponseBody
@RequestMapping("/hello")
publicString hello(@RequestParam("user") String user)
{
if (user.equals("aaa"))
{
thrownew UserNotExistException();
}
return "Hello";
}
}
自定义一个异常类:
public class UserNotExistException extendsRuntimeException{
public UserNotExistException() {
super("用户不存在");
}
}
添加一个统一异常处理类:
/**
* @author BaoZhou
* @date 2018/5/22
*/
@ControllerAdvice
public class MyExceptionHandler {@ExceptionHandler(UserNotExistException.class)
public String handlerException(Exception e, HttpServletRequest request) {
Map map = new HashMap<>();
//定义错误码,否则默认为200
request.setAttribute("javax.servlet.error.status_code",400);
map.put("code", "user.notexist");
map.put("message", "用户出错");
//将自定义map放入Attribute中
request.setAttribute("ext",map);
//重定向到error页面
return "forward:error";
}
}
自定义ErrorAttributes:
//给容器中加入我们自己定义的ErrorAttributes
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
/**
* 重写getErrorAttributes方法,将自己需要的错误讯息放入到map中,以便错误时能在map中获取
* 其中webRequest.getAttribute(para,para2))函数第二项表示从何处取数据
*int SCOPE_REQUEST = 0;
*int SCOPE_SESSION = 1;
*/
@Override
public Map getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map map = super.getErrorAttributes(webRequest, includeStackTrace);
map.put("user_name","baozhou");
map.put("ext",webRequest.getAttribute("ext",0));
return map;
}
文章图片
访问网页
文章图片
POSTMAN请求 就此大功告成!
推荐阅读
- 深入理解Go之generate
- Activiti(一)SpringBoot2集成Activiti6
- 标签、语法规范、内联框架、超链接、CSS的编写位置、CSS语法、开发工具、块和内联、常用选择器、后代元素选择器、伪类、伪元素。
- SpringBoot调用公共模块的自定义注解失效的解决
- 解决SpringBoot引用别的模块无法注入的问题
- 私有化轻量级持续集成部署方案--03-部署web服务(下)
- web网页模板|如此优秀的JS轮播图,写完老师都沉默了
- 我的软件测试开发工程师书单
- spring|spring boot项目启动websocket
- echart|echart 双轴图开发