程序员社区

Spring Boot 自定义异常内容


       在上篇文章: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;
    }
}

解析:

  1. Spring Boot 中已经自动帮我们配置了 BasicErrorController 组件,来帮我们处理/error 请求,请记住 Spring Boot 就是一个自适应的显示。如果想让我们自定义的异常解析器也变成自适应响应,此处我们只需要使用 forward 转发操作,来将我们自定义的错误转发到 /error 请求,然后由 Spring Boot 来帮我们处理,就可以实现异常的自适应显示了。

  2. 通过 request.setAttribute(“javax.servlet.error.status_code”,异常状态码); 的方式来传入我们的异常状态码。(为什么要传入异常状态码? 如果不定义状态码,虽然可以实现异常的自适应显示,但是现在仍然是无法来到我们自定义的异常页面,因为此时解析不出错误,因为返回的状态码 Status=200,显然这个返回的状态码是有问题的(参考图1)

  3. 这个状态码你不能随意设置,比如:999999,10001 等,必须是 Spring 定义的错误码(错误码见:HttpStatus 枚举类(共66个状态码))。我们在异常处理机制源码分析中发现:getStatus() 方法就是通过 request.getAttribute(“javax.servlet.error.status_code”) 的方式来获取的状态码,那么我们在此处就必须传入自己设置状态码了,不传的话,那就默认是 200 了,问题就在此处

  4. 现在可以正常进入到我们自定义的异常页面了,但是我们 code、message 等信息并没有为我们显示在异常中,这又需要怎么来解决?我们需要将定制的异常数据携带显示出去,接下来我们先围绕 SpringBoot 的错误处理原理机制,来分析一下。(参考图2)
     
            项目出现404、500 等错误以后,会来到 /error 请求。该请求就会被 BaseErrorController 是浏览器/客户端来返回Html页面/Json数据。返回的数据包括状态码、时间戳等数据,它是通过BaseErrorController.getErrorAttributes() ;方法来获取。在源码分析阶段,我们了解到 SpringBoot 默认给容器添加了四大重要组件,有一个 BaseErrorController 就是用来处理错误请求的,但是该组件要被自动配置是有条件的@ConditionalOnMissingBean(value = ErrorController.class, search =xxx),即组件中没有配置 ErrorController 时才会被自动配置。所以,此处我们就可以自定义一个 ErrorController 组件,就可以将我们定制的异常数据携带显示出去了。

  5. 处理方式共有两种: 编写一个 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组件

  6. 自定义异常处理器类MyExceptionHandlerMyErrorAttributes组件都编写完成,接下来就可以1.使用我们自定义异常页面2.实现异常自适应显示,测试如下图所示。(参考图3)

图片展示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



博主写作不易,来个关注呗

求关注、求点赞,加个关注不迷路 ヾ(◍°∇°◍)ノ゙

博主不能保证写的所有知识点都正确,但是能保证纯手敲,错误也请指出,望轻喷 Thanks♪(・ω・)ノ

赞(0) 打赏
未经允许不得转载:IDEA激活码 » Spring Boot 自定义异常内容

一个分享Java & Python知识的社区