在上篇文章:Spring Boot 错误处理机制源码分析,我们已经对 Spring Boot 错误处理机制的源码部分内容进行了详细的讲解,接下来我们来介绍如何自定义我们自己的异常内容提示
。
【建议:】学习本文前,如果你想要细致的理解 Spring Boot 针对错误异常的处理流程,建议你配合:Spring Boot 错误处理机制源码分析 来学习。
自定义异常,分以下两种情况来介绍:
1.使用模板引擎
Spring Boot 推荐使用 Thymeleaf,此处就以 Thymeleaf 为例来介绍。Thymeleaf 默认读取 classpath 下的 /template 目录,所以我们只需在 /template 目录下 创建一个 /error/404.html 页面即可。
发送 URL 请求,当遇到 404、500 等错误时,需要访问/error/404
时,①此时模板引擎就生效了,它会对路径进行拼接,拼接后的路径为:classpath:/template/error/404.html
。模板引擎就会去该路径下查找是否有一个 404.html
页面,有的话就会将其返回;②如果没使用模板引擎的话,就去Thymeleaf 规定的静态资源文件夹下找对应的 /error/404.html页面
;③都没有的话,返回 Spring Boot 为我们提供的默认错误页面。
2.自定义异常,让它返回我们指定的数据
这种情况下,根据是否需要自适应显示
,又分为两种情况。第一种:
不需要自适应显示,即:在浏览器和客户端访问时,都返回 Json 数据。第二种:
需要自适应显示,即:在浏览器访问,返回 Html 格式;在客户端访问时,返回 Json 数据格式。
我们先来自定义一个异常:
public class UserNotExistException extends RuntimeException {
public UserNotExistException() {
super("当前用户不存在");
}
}
编写一个 Controller,接下来就通过该 Controller 来发送请求。
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("/user/{userId}")
public String hello(@PathVariable("userId") int userId){
if(userId == 888){
throw new UserNotExistException();
}
return "Hello,"+userId;
}
}
2.1 不需要自适应显示
这种情况比较好解决。浏览器和客户端发送请求后,只需要同时返回 Json 数据即可。我们可以扩展 Spring MVC 功能,自定义一个异常处理器来处理返回的异常即可。
1.自定义异常处理器类MyExceptionHandler
@ControllerAdvice
public class MyExceptionHandler {
//1、浏览器客户端返回的都是json
//利用 Spring 的 Exception 机制,捕获到指定异常后,将需要写出的数据,通过 @ResponseBody写出
@ResponseBody
@ExceptionHandler(UserNotExistException.class)
public Map<String,Object> handleException(Exception e){
Map<String,Object> map = new HashMap<String,Object>();
map.put("code","1001");
map.put("message",e.getMessage());
map.put("cause",e.getCause());//此处可以自定义map,来显示更多的内容
return map;
}
不自适应很简单,只需要自定义一个上面的异常处理器即可。测试如下图所示:
2.2 需要自适应显示
这种情况相对来说就有点麻烦了。我们同样也需要扩展 Spring MVC 功能,来自定义一个异常处理器来处理返回的异常即可。
但是跟第一种方式的异常处理器就有一些稍微不同。
1.自定义异常处理器类MyExceptionHandler
该处理器是最终成型的代码,接下来我们就分段来介绍它们各行代码的意思。
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(UserNotExistException.class)
public String handleException(Exception e, HttpServletRequest request){
Map<String,Object> map = new HashMap<String,Object>();
//传入我们自己的错误状态码 4xx 5xx
request.setAttribute("javax.servlet.error.status_code",400);
map.put("code","1001");
map.put("msg","该用户不存在");
request.setAttribute("tip",map);
//转发到/error
return "forward:/error";
}
}
2.自定义MyErrorAttributes组件
//给容器中加入我们自己定义的ErrorAttributes
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
//返回值的map就是页面和json能获取的所有字段
@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
//调用父类方法,可以获得 状态码、时间戳等原有信息
Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
//然后,可以自定义一些我们想要的异常返回数据
map.put("name","扛麻袋的少年");
//(除上面company外),我们还需要将我们的异常处理器 MyExceptionHandler 携带的数据,也传出去
//我们可以将 自定义异常处理器 的数据,通过 request.setAttribute("ext",map); 方式放入 request 域。
//然后在此处通过 getAttribute 方式来获取。参数0:表示request域 1:表示 Session,可以在 RequestAttributes类中查看
Map<String,Object> tip= (Map<String, Object>) requestAttributes.getAttribute("tip",0);
map.put("tip",tip);
return map;
}
}
解析:
-
Spring Boot 中已经自动帮我们配置了 BasicErrorController 组件,来帮我们处理
/error
请求,请记住 Spring Boot 就是一个自适应的显示。如果想让我们自定义的异常解析器也变成自适应响应,此处我们只需要使用 forward 转发操作,来将我们自定义的错误转发到 /error 请求,然后由 Spring Boot 来帮我们处理,就可以实现异常的自适应显示了。
-
通过
request.setAttribute(“javax.servlet.error.status_code”,异常状态码);
的方式来传入我们的异常状态码。(为什么要传入异常状态码? 如果不定义状态码,虽然可以实现异常的自适应显示,但是现在仍然是无法来到我们自定义的异常页面,因为此时解析不出错误,因为返回的状态码 Status=200,显然这个返回的状态码是有问题的
)(参考图1) -
这个状态码你不能随意设置,比如:999999,10001 等,必须是 Spring 定义的错误码(错误码见:
HttpStatus 枚举类
(共66个状态码))。我们在异常处理机制源码分析中发现:getStatus() 方法就是通过 request.getAttribute(“javax.servlet.error.status_code”) 的方式来获取的状态码,那么我们在此处就必须传入自己设置状态码了,不传的话,那就默认是 200 了,问题就在此处
-
现在可以正常进入到我们自定义的异常页面了,但是我们 code、message 等信息并没有为我们显示在异常中,这又需要怎么来解决?
我们需要将定制的异常数据携带显示出去,接下来我们先围绕 SpringBoot 的错误处理原理机制,来分析一下。
(参考图2)
项目出现404、500 等错误以后,会来到/error
请求。该请求就会被BaseErrorController
是浏览器/客户端来返回Html页面/Json数据
。返回的数据包括状态码、时间戳等数据,它是通过BaseErrorController.getErrorAttributes() ;
方法来获取。在源码分析阶段,我们了解到 SpringBoot 默认给容器添加了四大重要组件,有一个BaseErrorController
就是用来处理错误请求的,但是该组件要被自动配置是有条件的@ConditionalOnMissingBean(value = ErrorController.class, search =xxx)
,即组件中没有配置 ErrorController 时才会被自动配置。所以,此处我们就可以自定义一个 ErrorController 组件,就可以将我们定制的异常数据携带显示出去了。 -
处理方式共有两种: ①编写一个 ErrorController 的实现类,将其通过注解
@Component
注册到容器中 ②我们发现 BasicErrorController 的父类是 AbstractErrorControllr 抽象类。所以我们也可以编写一个 AbstractErrorControllr 抽象类的实现,这样我们只需要写几个必要的方法即可。
显然这两种方式,还是需要我们写很多的代码来实现,我们继续来分析。我们又发现:异常返回值都是通过 getErrorAttributes() 方法得到的,我们发现 getErrorAttributes() 方法,具体调用的是 errorAttribute.getErrorAttributes() 方法的,所以我们可以通过改造 errorAttribute 重写方法来得到异常数据
。
errorAttribute 就是 ErrorAttribute类,该类就是容器中为我们自动配置的 ErrorAttribute。该组件要被自动配置也是有条件,@ConditionalOnMissingBean(value = ErrorAttributes.class, search = xxx
,即容器中没有 ErrorAttribute 组件时,DefaultErrorAttributes 组件才会被注入。重点:既然所有的数据都是通过 DefaultErrorAttributes 组件获取的,那么我们只要自定义一个 ErrorAttribute 组件,就可以对异常携带的数据进行操作了。我们就来自定义一个 MyErrorAttributes ,继承 DefaultErrorAttributes 类,重写 getErrorAttributes() 方法即可。见上文中自定义MyErrorAttributes组件
-
自定义异常处理器类
MyExceptionHandler
、MyErrorAttributes组件
都编写完成,接下来就可以1.使用我们自定义异常页面
、2.实现异常自适应显示
,测试如下图所示。(参考图3)
图片展示:
博主写作不易,来个关注呗
求关注、求点赞,加个关注不迷路 ヾ(◍°∇°◍)ノ゙
博主不能保证写的所有知识点都正确,但是能保证纯手敲,错误也请指出,望轻喷 Thanks♪(・ω・)ノ