zoukankan      html  css  js  c++  java
  • SpringBoot 统一异常处理

    1.前言

    在做项目的时候,常常会遇到这些情况,

    ​ (一)、繁多的if..else 造成大量的参数代码判断,于是就在实体类字段中添加@NotBlank,@NotNull等注解代替,可是注解上自定义的message 消息无法规范的返回到前端;

    ​ (二)、在业务层代码中,当方法层层嵌套,对最深处的代码进行不满足的参数做判断时,直接返回响应体并不是很合适(这个时候就需要抛出自定义异常)

    ​ (三)、通过Assert 断言去除冗余的if..else 时,发现断言抛出的异常也没有规范的返回前端;

    ​ 为了解决这些情况,我们需要做一个统一异常处理,无论是注解的异常抛出,自定义异常抛出、以及断言的异常抛出都需要统一获取其中的message。这个统一异常处理即今天所要说的 @ExceptionHandler+@ControllerAdvice ,统一异常处理。

    2.直接实战

    原理也不解释,废话也不多说,直接记录怎么配置,怎么使用。想知道原理的,自行百度。

    2.1.创建项目

    ​ 通过IDEA 中的SpringInitializr 创建springboot项目,并一次创建各个层的文件夹,controller、service、entity、model、exception、handler等文件夹。如下所示:

    2.2 统一异常配置

    pom.xml 添加 以下依赖

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.7</version>
    </dependency>
    
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>
    
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.4.1.Final</version>
    </dependency>
    

    2.2.1 Result 响应实体类,用来返回前端的实体类。

    /**
     * @Author: DZBiao
     * @Date : 2021/12/11
     * @Description : 描述:
     **/
    public class Result {
    
        /**
         * 响应状态码
         */
        private Integer code;
        /**
         * 响应成功与否
         */
        private boolean success;
        /**
         * 响应消息
         */
        private String msg;
        /**
         * 响应数据
         */
        private Object data;
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    
        public boolean isSuccess() {
            return success;
        }
    
        public void setSuccess(boolean success) {
            this.success = success;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public Object getData() {
            return data;
        }
    
        public void setData(Object data) {
            this.data = data;
        }
    
        public Result() {
        }
    
        public Result(Integer code, boolean success, String msg) {
            this.code = code;
            this.success = success;
            this.msg = msg;
        }
    
        public Result(Integer code, boolean success, String msg, Object data) {
            this.code = code;
            this.success = success;
            this.msg = msg;
            this.data = data;
        }
    
        /**
         * 成功 返回默认成功信息
         *
         * @return
         */
        public static Result SUCCESS() {
            return new Result(1, true, "操作成功", null);
        }
    
        /**
         * 成功 返回(data数据)成功信息
         *
         * @param data
         * @return
         */
        public static Result SUCCESS(Object data) {
            return new Result(1, true, "操作成功", data);
        }
    
        /**
         * 成功 返回自定义(消息、data数据)成功信息
         *
         * @param msg
         * @param data
         * @return
         */
        public static Result SUCCESS(String msg, Object data) {
            return new Result(1, true, msg, data);
        }
    
        /**
         * 失败 返回默认失败信息
         *
         * @return
         */
        public static Result ERROR() {
            return new Result(-1, false, "操作失败", null);
        }
    
        /**
         * 失败 返回自定义(消息)失败信息
         *
         * @param msg
         * @return
         */
        public static Result ERROR(String msg) {
            return new Result(-1, false, msg, null);
        }
    
        /**
         * 失败 返回自定义(消息、状态码)失败信息
         *
         * @param code
         * @param msg
         * @return
         */
        public static Result ERROR(Integer code, String msg) {
            return new Result(code, false, msg, null);
        }
    }
    
    

    2.2.2 @Controller 和 @RestControllerAdvice 配置

    在controller层中新建IndexController类,传参实体类使用简单的User类;

    @RestController
    @RequestMapping("/index")
    public class IndexController {
    
    
        @PostMapping("/list")
        public Result index(@Valid @RequestBody User user) throws Exception {
    
            return Result.SUCCESS();
    
        }
    
    }
    

    实体类User ,当我想要在传参时进行非空判断,则添加@NotBlank注解,并在Controller层 添加@Valid和@RequestBody 注解。(@Data 注解为Lombok 插件,idea中安装,如果没有直接在实体类中生成get和set 方法代替.)

    @Data
    public class User {
    
        private String id ;         // ID
    
        @NotBlank(message = "用户名不能为空.")
        private String username ;   // 用户名
    
        @NotBlank(message = "性别不能为空")
        private String sex ;  // 性别
    
        @NotBlank(message = "地址不能为空")
        private String address ;    // 地址
    
        private String status ;
    
    
    }
    

    在handler 文件夹中新建GlobalExceptionController类,并配置@RestControllerAdvice注解。对于实体类参数上的注解的异常捕获,我们在统一异常处理类中,通过MethodArgumentNotValidException进行捕获处理。

    @RestControllerAdvice
    public class GlobalExceptionController {
    	
        
            /**
         * 处理方法参数异常,如 实体类上的@NotBlank注解异常,@NotNull注解等异常信息返回。
         * @param ex
         * @return
         */
        @ExceptionHandler(MethodArgumentNotValidException.class)
        public Result handleMethodArgumentException(MethodArgumentNotValidException  ex){
    
            return Result.ERROR(ex.getBindingResult().getFieldError().getDefaultMessage()) ;
        }
    }
    

    至此我们解决了三个问题中的其中一个问题。下面解决抛出自定义异常 和断言异常的捕获。

    在GlobalExceptionController中再添加 处理自定义异常和断言异常的捕获方法。

    @RestControllerAdvice
    public class GlobalExceptionController {
    	
        
            /**
         * 处理方法参数异常,如 实体类上的@NotBlank注解异常,@NotNull注解等异常信息返回。
         * @param ex
         * @return
         */
        @ExceptionHandler(MethodArgumentNotValidException.class)
        public Result handleMethodArgumentException(MethodArgumentNotValidException  ex){
    
            return Result.ERROR(ex.getBindingResult().getFieldError().getDefaultMessage()) ;
        }
        
        
            /**
         * 断言、自定义异常等处理
         * @param ex
         * @return
         */
        @ExceptionHandler(Exception.class)
        public Result handleException(Exception  ex){
    
            return Result.ERROR(ex.getMessage()) ;
        }
       
    }
    

    3.测试

    我们在exception文件夹中创建自定义异常类:CustomException,并继承RuntimeException

    public class CustomException extends RuntimeException {
    
        private String message ;
    
        public CustomException(String message){
            this.message = message ;
        }
    
        @Override
        public String getMessage() {
            return message;
        }
    
        public CustomException setMessage(String message) {
            this.message = message;
            return this;
        }
    }
    

    在controller中添加一些判断进行观察,看看我们配置的情况。

    @RestController
    @RequestMapping("/index")
    public class IndexController {
    
    
        @PostMapping("/list")
        public Result index(@Valid @RequestBody User user) throws Exception {
    
            // 抛出Exception异常
            if (user.getUsername().equals("xxx")){
                throw new Exception("用户名错误.");
            }
    
            // 自定义异常
            if (user.getUsername().equals("xxxxx")){
                throw new CustomException("自定义异常");
            }
    
            // 断言异常
            Assert.notNull(user.getStatus(), "状态不能为空");
    
            return Result.SUCCESS();
    
        }
    
    }
    

    我们通过Postman或者ApiPost观察。当我们什么参数都不传时,就会捕获注解上的异常message,当username传“xxx”,就会捕获用户名错误.异常,同理,可以观察到其他的情况,不多赘述。

    统一异常处理配置很简单。至此配置完成。

    通过统一异常处理的配置,我们可以去除大量的if..else 冗余代码,全部交由注解或者Assert断言,或者自定义异常来解决。

    需要注意的一点是:该@Valid 注解 和@NotBlank 注解 是在springboot3.6以下版本基础之上的。本地demo使用的版本是2.6.1,springboot3.6 需要使用 @Validated 注解 。

  • 相关阅读:
    linux转换win下乱码txt命令
    linux下vi命令大全详细版本
    ubuntu系统如何安装adb调试环境
    LeetCode136---只出现一次的数字
    微信发朋友圈--用例设计(转)
    微服务
    LeetCode1---两数之和
    python输出
    爬楼梯,N级楼梯有多少种走法?
    list数组排序---stream
  • 原文地址:https://www.cnblogs.com/duanxiaobiao/p/15679110.html
Copyright © 2011-2022 走看看