1、 默认处理机制
根据 Http 请求头(Request Headers )中,Accept
中信息决定返回 html 页面还是 json 数据。
- 浏览器请求
text/html
– 返回html页面
默认返回页面(html白页)
- 其他客户端(postman)请求
*/*
– 返回json数据
默认返回的json信息
2、默认处理原理
- 主要参见
ErrorMvcAutoConfiguration
自动配置类
// 条件注入组件: DefaultErrorAttributes
@Bean
@ConditionalOnMissingBean(
value = {ErrorAttributes.class},
search = SearchStrategy.CURRENT
)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
}
// 条件注入组件: BasicErrorController
@Bean
@ConditionalOnMissingBean(
value = {ErrorController.class},
search = SearchStrategy.CURRENT
)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
return new BasicErrorController(errorAttributes,
this.serverProperties.getError(), this.errorViewResolvers);
}
// ErrorPageCustomizer(产生错误,发出什么处理请求)
@Bean
public ErrorMvcAutoConfiguration.ErrorPageCustomizer errorPageCustomizer() {
return new ErrorMvcAutoConfiguration.ErrorPageCustomizer(this.serverProperties,
this.dispatcherServletPath);
}
// 默认处理页
static class StaticView {...}
- ErrorPageCustomizer
private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {
......
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
// 这里默认的请求 /error
ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath
.getRelativePath(this.properties.getError().getPath()));
errorPageRegistry.addErrorPages(new ErrorPage[]{errorPage});
}
}
-
BasicErrorController:处理默认/error请求
当产生错误时,会去请求
/error
@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
......
// 产生html类型的数据。浏览器发送的请求来到这个方法处理
@RequestMapping(
produces = {"text/html"}
)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = this.getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
// 去哪个页面作为错误页面;包含页面地址和页面内容
ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
return modelAndView != null ? modelAndView : new ModelAndView("error", model);
}
// 产生json数据,其他客户端来到这个方法处理
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = this.getStatus(request);
return new ResponseEntity(body, status);
}
}
- DefaultErrorViewResolver:默认错误视图解析
public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
...
// 视图解析
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
private ModelAndView resolve(String viewName, Map<String, Object> model) {
// 默认寻找 error/xxx页面,如 error/404
String errorViewName = "error/" + viewName;
// 模板引擎可以解析这个页面地址就用模板引擎解析
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
.getProvider(errorViewName, this.applicationContext);
// 模板引擎可用返回指定的视图模型,否则在资源文件夹下寻找页面
return provider != null ? new ModelAndView(errorViewName, model)
: this.resolveResource(errorViewName, model);
}
}
-
DefaultErrorAttributes: 提供的默认错误信息
帮助我们在页面展示出错信息。
public class DefaultErrorAttributes
implements ErrorAttributes, HandlerExceptionResolver, Ordered {
......
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap();
errorAttributes.put("timestamp", new Date()); // 错误产生的时间
this.addStatus(errorAttributes, webRequest); // 状态码等
this.addErrorDetails(errorAttributes, webRequest, includeStackTrace); // 出错信息
this.addPath(errorAttributes, webRequest);
return errorAttributes;
}
......
}
3、自定义错误页面
在模板文件夹templates
下,新建error
文件夹,里面放置错误页面,匹配时由精确到模糊。
4xx.html
模糊,4开头类型错误的处理页面5xx.html
模糊,5开头类型错误的处理页面404.html
精确,404类型,错误的处理页面
4、异常跳转错误页面
在@ControllerAdvice
注解下的类,里面的方法用@ExceptionHandler
注解修饰的方法,会将对应的异常交给对应的方法处理。
注意:
@ExceptionHandler
注解参数可以传入数组,处理多种异常- 不能使用两个
@ExceptionHandler
处理同一种异常
@ControllerAdvice
public class CustomizeExceptionHandler {
// 返回渲染的视图
@ExceptionHandler(CustomizeException.class) // 处理自定义CustomizeException异常
ModelAndView handle(HttpServletRequest request, Throwable e, Model model) {
model.addAttribute("message", e.getMessage());
return new ModelAndView("/error");
}
// 返回json数据
@ResponseBody
@ExceptionHandler(UserNotExistException.class) // 处理自定义UserNotExistException异常
ModelAndView handle(HttpServletRequest request, Throwable e, Model model) {
Map<String, String> map = new HashMap<>();
map.put("code", "not exist");
map.put("msg", e.getMessage());
return map;
}
}
5、 定制自适应错误页面
出现错误以后,会来到/error
请求,会被BasicErrorController处理,响应出去可以获取的数据是由
getErrorAttributes
得到的(是AbstractErrorController(ErrorController)规定的方法)
页面上能用的数据,或者是json返回能用的数据都是通过getErrorAttributes
得到;
容器中默认使用DefaultErrorAttributes.getErrorAttributes()
得到,我们可以继承DefaultErrorAttributes
来自定义返回的数据。
// 自定义异常
public class UserNotExistException extends RuntimeException {
public UserNotExistException() {
super("用户找不到");
}
}
// 请求处理出现异常
@ResponseBody
@RequestMapping("/aaa")
public String aaa(@RequestParam("user") String user) {
if ("aaa".equals(user)) {
throw new UserNotExistException(); //抛出自定义异常
}
return "/";
}
// 异常被捕获
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(UserNotExistException.class) // 处理自定义UserNotExistException异常
public String handleUserNotExistException(Exception e, HttpServletRequest request) {
Map<String, String> map = new HashMap<>();
// 重要: 传入我们自己的错误状态码
request.setAttribute("javax.servlet.error.status_code", 500);
map.put("code", "not exist"); // 一些该异常的错误信息
map.put("msg", e.getMessage());
// 把数据放入request域
request.setAttribute("ext", map);
return "forward:/error";
}
}
// 容器中添加自定义的ErrorAttributes,不再使用默认的DefaultErrorAttributes
// 获得不同客户端自适应效果
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
// 返回的map就是页面和json能获取的所有字段
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
map.put("company", "moc"); // 自定义通用的数据
// 加入异常处理器携带的数据
Map<String, Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", 0);
map.put("ext", ext);
return map;
}
}
// 页面获取数据(thymeleaf)
<body>
<h1>status:[[${status}]]</h1>
<h1>timestamp:[[${timestamp}]]</h1>
<h1>trace:[[${trace}]]</h1>
<h1>message:[[${message}]]</h1>
<h1>company:[[${company}]]</h1>
<h1>ext:[[${ext.code}]]</h1>
<h1>ext:[[${ext.msg}]]</h1>
</body>
- map数据