错误页面
1. 文件路径
classpath:templates/error
:模板存放地址
classpath:static/error
:静态页面存放地址
- 无需额外配置,Spring-Boot自动识别
- 文件名error,没有需要手动创建
server.error.path
配置可以替换error
,看情况配置- 优先采用模板,没有再找静态文件夹
2. 文件格式
- 指定错误页
# 404跳转页面
404.html
# 500跳转页面
500.html
文件名格式:
code
.html
- 默认错误页
# 4XX错误统一错误页面
4xx.html
# 5xx错误统一页面
5xx.html
- 有详细错误页面404.html,优先采用详细错误页面,没有则采用4xx.html
- 40x.html之类的没试过,可以看看行不行
3. 自动路由
存在错误页面就行了,多余的不需要再进行配置,能够直接生效。
4. 模板渲染
- 静态页面
不论是classpath:static
或者classpath:templates
都能够被系统映射并利用。 - 模板渲染
但是系统为模板注入了几个错误的显示信息:
-
timestamp
:时间戳 -
status
:状态码 -
error
:错误信息 -
exception
:异常对象 -
message
:提示信息 -
errors
:JSR303
当采用模板编写错误页面时,可以直接引入这些已经注入的错误信息。
但是如果错误页面在
classpath:static
静态文件夹下的话,是取不到这些信息的。
5. 默认提示
如果都没有配置,那就是Spring-Boot自己的默认提示页了。
错误数据
1. 自定义异常
public class MyException extends Exception{
public MyException(){
super("MyException");
}
}
public class MyException extends Exception{
public MyException(){
super("MyException");
}
}
2. json输出
@ControllerAdvice
public class MyHandlerException {
@ResponseBody
@ExceptionHandler(MyException.class)
public Map<String,Object> handlerException(Exception e){
Map<String ,Object> map = new HashMap<>();
map.put("myMessage","自定义信息" );
return map;
}
}
@ControllerAdvice
public class MyHandlerException {
@ResponseBody
@ExceptionHandler(MyException.class)
public Map<String,Object> handlerException(Exception e){
Map<String ,Object> map = new HashMap<>();
map.put("myMessage","自定义信息" );
return map;
}
}
@ControllerAdvice
:错误跳转控制器
@ResponseBody
:返回内容
@ExceptionHandler
:处理的异常类,针对指定异常进行处理
Map
:返回信息,自动放进域内
- 直接返回的
json
信息,只包含自动一内容myMessage
- Spring-Boot默认数据未注入
- 浏览器和接口访问都只返回
json
3. 适配输出
@ControllerAdvice
public class MyHandlerException {
@ExceptionHandler(MyException.class)
public String handlerException(Exception e, HttpServletRequest request){
Map<String, Object> map = new HashMap<>();
map.put("msg","error");
request.setAttribute("javax.servlet.error.status_code", 400);
return "redirect:/error";
}
}
@ControllerAdvice
public class MyHandlerException {
@ExceptionHandler(MyException.class)
public String handlerException(Exception e, HttpServletRequest request){
Map<String, Object> map = new HashMap<>();
map.put("msg","error");
request.setAttribute("javax.servlet.error.status_code", 400);
return "redirect:/error";
}
}
redirect:/error
:Spring-Boot自动维护一个BasicErrorController
,redirect:/error
会转发到那让它自动处理
request
:BasicErrorcontroller
从请求域中识别错误类型
javax.servlet.error.status_code
:为了让BasicErrorController
能够识别,需要传入该属性,设置错误码
redirect:/error
:为了流程完整,再说一遍,转发BasicErrorController
进行处理
- 转发
BasicErrorController
的话,就会自动识别浏览器和接口,自动跳转页面和返回json
了- 但是携带的只有Spring-Boot定制的基本信息,我们可以修改,但是新增的信息不回传达到显示
4. 信息携带
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
map.put("add_msg","this is new add msg");
map.put("company", "godme");
Map<String, Object> myMessage = (Map<String, Object>) webRequest.getAttribute("myMsg", RequestAttributes.SCOPE_REQUEST);
map.put("myMessage", myMessage);
return map;
}
}
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
map.put("add_msg","this is new add msg");
map.put("company", "godme");
Map<String, Object> myMessage = (Map<String, Object>) webRequest.getAttribute("myMsg", RequestAttributes.SCOPE_REQUEST);
map.put("myMessage", myMessage);
return map;
}
}
DefaultErrorAttributes
:BasicErrorController
取的信息都是从DefaultErrorAttributes
中取得,所以需要我们进行注入
company
:一般开发的话,总有公司想要注入公司名称。
webReqeust
:这个是从web中去获取数据的请求对象
getAttribute
:从域中获取数据,需要指定key
,和scope
存储的域,从指定域中获取对象
return
:返回的map
,BasicErrorController
会自动处理的啦,自动写入json
,也会自动注入模板
5. 错误纠正
为了方便,一直说的是BasicErrorController
,顾名思义,它只是Controller
而已。真正的处理不是它,而是路由到的指定处理器。
如果有想法,可以顺着逻辑去翻一翻源码。
6. 总结梳理
1. 一般配置
- 页面位置
classpath:templates/error
claspath:static/error
模板位置可以配置:
spring.server.error.path
- 文件名称
- 错误页面:
code.html
- 默认页面:
nxx.html
- 模板数据
-
static
;无数据注入 -
templates
:自动注入数据
BasicErrorController
自动路由
2. 自定数据
- 定义异常
public class MyException extends Exception{
public MyException(){
super("MyException");
}
}
public class MyException extends Exception{
public MyException(){
super("MyException");
}
}
- 信息注入
@ControllerAdvice
public class MyHandlerException {
@ExceptionHandler(MyException.class)
public String handlerException(Exception e, HttpServletRequest request){
Map<String, Object> map = new HashMap<>();
request.setAttribute("myMessage",e.getMessage());
request.setAttribute("javax.servlet.error.status_code", 400);
return "redirect:/error";
}
}
@ControllerAdvice
public class MyHandlerException {
@ExceptionHandler(MyException.class)
public String handlerException(Exception e, HttpServletRequest request){
Map<String, Object> map = new HashMap<>();
request.setAttribute("myMessage",e.getMessage());
request.setAttribute("javax.servlet.error.status_code", 400);
return "redirect:/error";
}
}
@ControllerAdvice
:异常访问
ExceptionHandler
:异常捕捉
request.setAttribute("myMessage",e.getMessage())
:信息注入
request.setAttribute("javax.servlet.error.status_code", 400)
:路由标记
- 信息提取
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
map.put("add_msg","this is new add msg");
map.put("company", "godme");
Map<String, Object> myMessage = (Map<String, Object>) webRequest.getAttribute("myMsg", RequestAttributes.SCOPE_REQUEST);
map.put("myMessage", myMessage);
return map;
}
}
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
map.put("add_msg","this is new add msg");
map.put("company", "godme");
Map<String, Object> myMessage = (Map<String, Object>) webRequest.getAttribute("myMsg", RequestAttributes.SCOPE_REQUEST);
map.put("myMessage", myMessage);
return map;
}
}
DefaultErrorAttributes
:默认采用信息
map.put("company", "godme")
:注入额外信息
webRequest.getAttribute("myMsg", RequestAttributes.SCOPE_REQUEST)
:提取注入信息
map.put("myMessage", myMessage)
:添加到处理参数
@Component
:别忘记标记,交给Spring
3. 流程总结
- 三重路由
- 页面访问
- 程序异常处理
- 错误转发
BasicErrorController
- 数据注入
- 页面数据不相关
- 异常处理时注入新信息
BasicErrorController
提取新信息并设置
- 关键步骤
- 入口
@ControllerAdvice
:控制错误跳转
- 注入
@ExceptionHandler
:处理异常,完成信息注入
和错误转发
- 交由
BasicErrorController
处理,注意注入错误信息,便于路由
- 提取
DefaultErrorAttributes
重写getErrorAttributes
getErrorAttributes
:提取上层注入信息,注入
@Component
:Spring管理
三种程度
1. 简单配置
简单添加页面,无需配置,利用程度不高,具体信息无法获取。
2. 手动修改
自定义信息,不论访问方式,都是json
返回,无页面路由,浏览器页面不友好。
3. 嫁接方案
- 异常拦截
- 请求转发
通过拦截异常,把请求转发BasicErrorController。
利用Spring-Boot自己自动路由功能完成页面和接口的差异化适配。
request.setAttribute
DefaultAttributes
通过scope
设置域参数,再注入到BasicErrorController
中,让自定义参数得以处理。
本质上来说,三种处理方式如下
- 入口提供路由页面,Spring-Boot自动路由
- 手动路由,新参数注入但未适配客户端
- 修改Spring-Boot错误路由,提供页面,手动路由,注入参数
第三种方式关键点在于
手动路由
和参数注入
手动路由
:让Spring-Boot自动适配客户端
参数注入
:让Spring-Boot能够采用新参数