• springboot接口参数校验(JSR303)


    在 javax.validation.constraints包中定义了非常多的校验注解,引入依赖:

    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

    在实体类上添加对应注解即可,
    在这里插入图片描述

    实体类:

    package com.zhmsky.mallproduct.entity;
    
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    
    import java.io.Serializable;
    import java.util.Date;
    import lombok.Data;
    
    import javax.validation.constraints.NotBlank;
    
    /**
    * 品牌
    * 
    * @author zhmsky
    * @email zhmsky@gmail.com
    * @date 2022-07-30 16:40:28
    */
    @Data
    @TableName("pms_brand")
    public class BrandEntity implements Serializable {
        private static final long serialVersionUID = 1L;
    
        /**
        * 品牌名
        */
        @NotBlank(message = "品牌名不能为空")
        private String name;
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    一、在controller接口处理校验异常

    message可指定校验说明,
    接下来在对应的controller接口方法中使用@Valid 注解来指定接口参数校验,后面紧跟BindingResult来接收校验结果,当发生校验失败则封装并返回校验失败信息,如果通过校验则执行相关逻辑

    @RequestMapping(value = "/save",method = RequestMethod.POST)
    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
        if(result.hasErrors()){
            Map<String, String> map = new HashMap<>();
            //获取校验结果
            for (FieldError fieldError : result.getFieldErrors()) {
                //获取错误提示
                String message = fieldError.getDefaultMessage();
                //校验错误的字段
                String field = fieldError.getField();
                //封装错误
                map.put(field,message);
            }
            return R.error(20001,"参数校验失败").put("errorMap",map);
        }
    	brandService.save(brand);
        return R.ok();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    二、统一异常处理

    如果在每一个接口都进行参数校验异常处理,工作量比较大,并且代码比较繁琐,于是引入全局异常处理,
    也就是@RestControllerAdvice+@ExceptionHandler注解的使用。

    package com.zhmsky.mallproduct.exception;
    
    import com.zhmsky.common.utils.R;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.validation.BindingResult;
    import org.springframework.validation.FieldError;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 同意异常处理类
     * @author zhmsky
     * @date 2022/8/1 21:36
     */
    @Slf4j
    @RestControllerAdvice(basePackages = {"com.zhmsky.mallproduct.controller"})
    public class MallProductControllerAdvice {
    
        //具体的某个异常类型处理
        @ExceptionHandler(MethodArgumentNotValidException.class)
        public R handleValidException(MethodArgumentNotValidException e){
            log.error("数据异常:{},异常类型:{}",e.getMessage(),e.getClass());
            BindingResult result = e.getBindingResult();
            Map<String, String> map = new HashMap<>();
            for (FieldError fieldError : result.getFieldErrors()) {
                map.put(fieldError.getDefaultMessage(),fieldError.getField());
            }
            return R.error().put("errorMap",map);
        }
    
        //抛出的所有异常类型处理
        @ExceptionHandler(Throwable.class)
        public R handleException(Throwable e){
            //TODO 处理逻辑
            return R.error();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    三、错误码枚举类

    package com.zhmsky.common.exception;
    
    /**
     * @author zhmsky
     * @date 2022/8/1 21:56
     */
    public enum ErrorCodeEnum {
      
        UNKNOWN_EXCEPTION(10000, "系统未知错误"),
    
        VALID_EXCEPTION(10001, "参数格式校验失败");
    
        /**
         * 错误码
         */
        private Integer code;
    
        /**
         * 错误提示
         */
        private String msg;
    
        ErrorCodeEnum(Integer code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    
        public Integer getCode(){
            return this.code;
        }
    
        public String getMsg(){
            return this.msg;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    四、自定义参数校验注解

    1、编写自定义校验注解

    参照javax.validation.constraints的@NotBlank注解,

    @Documented
    @Constraint(validatedBy = { })
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Repeatable(List.class)
    public @interface NotBlank {
        //校验规则提示信息
    	String message() default "{javax.validation.constraints.NotBlank.message}";
        
        //分组校验
    	Class<?>[] groups() default { };
    
        //负载
    	Class<? extends Payload>[] payload() default { };
    
    	/**
    	 * Defines several {@code @NotBlank} constraints on the same element.
    	 *
    	 * @see NotBlank
    	 */
    	@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    	@Retention(RUNTIME)
    	@Documented
    	public @interface List {
    		NotBlank[] value();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    模仿上面可快速编写一个自定义注解:

    @Documented
    @Constraint(validatedBy = {StatusValueConstraintValidator.class})
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    public @interface statusValue {
        //校验规则提示信息(在配置文件中配置)
        String message() default "{com.zhmsky.common.validator.statusValue.message}";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
        //注解参数
        int[] values() default {};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    创建 ValidationMessages.properties 文件,在配置文件中配置自定义的校验规则提示信息:

    com.zhmsky.common.validator.statusValue.message=error.......xxxx
    
    • 1

    2、编写自定义校验器

    //statusValue就是自定义的注解,Integer就是自定义注解标注的字段类型
    public class StatusValueConstraintValidator implements ConstraintValidator<statusValue, Integer> {
        
        private Set<Integer> set = new HashSet<>();
        
        //初始化方法
        @Override
        public void initialize(statusValue constraintAnnotation) {
            //字段标注的注解里面设定的值(校验规则)
            int[] values = constraintAnnotation.values();
            for (int value : values) {
                set.add(value);
            }
        }
        
        //判断是否校验成功
        @Override
        public boolean isValid(Integer value, ConstraintValidatorContext context) {
            //value就是字段字段实际接收到的值
            return set.contains(value);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3、关联自定义注解和自定义校验器

    也就是在自定义注解的@Constraint注解中指明自定义的校验器,
    在这里插入图片描述

    4、测试自定义注解@statusValue

    @Data
    @TableName("pms_brand")
    public class BrandEntity implements Serializable {
    	private static final long serialVersionUID = 1L;
        
        	/**
    	 * 品牌名
    	 */
    	@NotBlank(message = "品牌名不能为空")
    	private String name;
        
        
    	/**
    	 * 显示状态[0-不显示;1-显示],指定字段值只能为 0 或 1
    	 */
    	@statusValue(values={0,1})
    	private Integer showStatus;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在对应的controller接口中使用@Validated注解来进行校验,showStatus字段传入值3,
    测试结果如图:
    在这里插入图片描述

  • 相关阅读:
    java开发中 防止重复提交的几种方案
    【UCIe】UCIe Sideband 介绍
    Android Jetpack解析之——LiveData
    Django 更新数据 save()方法
    你是如何使用背景和文本属性的呢 ,如果还不太熟悉的话可以来看看我的喔。
    java毕业设计师生健康信息管理系统mybatis+源码+调试部署+系统+数据库+lw
    C/C++图书管理系统
    基于Dockerfile创建镜像
    Apache Spark 的基本概念和在大数据分析中的应用
    maven依赖找不到的解决:手动下载、多镜像导入。
  • 原文地址:https://blog.csdn.net/weixin_42194695/article/details/126128963