• 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 注解 。

  • 相关阅读:
    606. Construct String from Binary Tree
    696. Count Binary Substrings
    POJ 3255 Roadblocks (次短路)
    POJ 2823 Sliding Window (单调队列)
    POJ 1704 Georgia and Bob (博弈)
    UVa 1663 Purifying Machine (二分匹配)
    UVa 10801 Lift Hopping (Dijkstra)
    POJ 3281 Dining (网络流之最大流)
    UVa 11100 The Trip, 2007 (题意+贪心)
    UVaLive 4254 Processor (二分+优先队列)
  • 原文地址:https://www.cnblogs.com/duanxiaobiao/p/15679110.html
Copyright © 2020-2023  润新知