• (十)admin-boot项目之validation验证入参规则


    (十)validation验证入参规则

    项目地址:https://gitee.com/springzb/admin-boot
    如果觉得不错,给个 star

    简介:
    这是一个基础的企业级基础后端脚手架项目,主要由springboot为基础搭建,后期整合一些基础插件例如:redis、xxl-job、flowable、minioio、easyexcel、skyWalking、rabbitmq

    一、引入maven依赖

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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ky0vZ78G-1660445816590)(image/image_HaW6A_i7sC.png)]

    二、编写样例

    全局异常新增捕获,参数验证异常:

    package cn.mesmile.admin.common.handler;
    
    import cn.mesmile.admin.common.constant.AdminConstant;
    import cn.mesmile.admin.common.exceptions.*;
    import cn.mesmile.admin.common.result.R;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.validation.BindException;
    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 javax.validation.ConstraintViolation;
    import javax.validation.ConstraintViolationException;
    import java.util.stream.Collectors;
    
    /**
     * @author zb
     * @Description 全局异常拦截
     * 

    * 如果我同时捕获了父类和子类,那么到底能够被那个异常处理器捕获呢?比如 Exception 和 BusinessException * 当然是 BusinessException 的异常处理器捕获了,精确匹配,如果没有 BusinessException 的异常处理器才会轮到它的 父亲 , * 父亲 没有才会到 祖父 。总之一句话, 精准匹配,找那个关系最近的 *

    */
    @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { /** * @param businessException 业务异常 * @return @ResponseBody * @ExceptionHandler相当于controller的@RequestMapping 如果抛出的的是BusinessException,则调用该方法 */ @ExceptionHandler(BusinessException.class) public R handle(BusinessException businessException) { // 获取指定包名前缀的异常信息,减少不必要的日志 String stackTraceByPn = getStackTraceByPn(businessException, AdminConstant.BASE_PACKAGE); log.error("记录业务异常信息: 消息{} 编码{} {}", businessException.getMessage(), businessException.getCode(), stackTraceByPn); return R.fail(businessException.getCode(), businessException.getMessage()); } @ExceptionHandler(ServiceException.class) public R handle(ServiceException serviceException) { // 这里记录所有堆栈信息 log.error("记录业务异常信息: 消息{} 编码{}", serviceException.getMessage(), serviceException.getCode(), serviceException); return R.fail(serviceException.getCode(), serviceException.getMessage()); } private String getStackTraceByPn(Throwable e, String packagePrefix) { StringBuilder append = new StringBuilder("\n").append(e); for (StackTraceElement stackTraceElement : e.getStackTrace()) { if (stackTraceElement.getClassName().startsWith(packagePrefix)) { append.append("\n\tat ").append(stackTraceElement); } } return append.toString(); } // 捕获参数验证异常 @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class}) public R handleValidatedException(Exception exception) { BindingResult bindingResult = null; if (exception instanceof MethodArgumentNotValidException){ MethodArgumentNotValidException e = (MethodArgumentNotValidException) exception; bindingResult = e.getBindingResult(); if (bindingResult.hasErrors()) { // String collect = bindingResult.getAllErrors().stream() // .map(ObjectError::getDefaultMessage) // .collect(Collectors.joining(";")); FieldError fieldError = bindingResult.getFieldError(); if (fieldError != null) { return R.fail(fieldError.getField()+ ":" + fieldError.getDefaultMessage()); } } }else if (exception instanceof ConstraintViolationException){ ConstraintViolationException e = (ConstraintViolationException) exception; String collect = e.getConstraintViolations().stream() .map(ConstraintViolation::getMessage) .collect(Collectors.joining(";")); return R.fail(collect); }else if (exception instanceof BindException){ BindException e = (BindException) exception; bindingResult = e.getBindingResult(); if (bindingResult.hasErrors()) { // String collect = bindingResult.getAllErrors().stream() // .map(ObjectError::getDefaultMessage) // .collect(Collectors.joining(";")); FieldError fieldError = bindingResult.getFieldError(); if (fieldError != null) { return R.fail(fieldError.getField()+ ":" + fieldError.getDefaultMessage()); } } } return R.fail(exception.getMessage()); } /** * 捕获空指针异常 **/ @ExceptionHandler(value = NullPointerException.class) public R handlerBindException(NullPointerException exception) { String message = exception.getMessage(); log.error("全局捕获null错误信息: {}", exception.toString(), exception); return R.fail(message); } /** * 捕获最大异常 **/ @ExceptionHandler(value = Exception.class) public R handlerBindException(Exception exception) { String message = exception.getMessage(); log.error("全局捕获错误信息: {}", exception.toString(), exception); return R.fail(message); } }
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119

    验证嵌套验证

    /**
     * @author zb
     * @Description 测试验证
     */
    @Data
    public class Student {
    
        @Length(min = 6,max = 12,message = "username长度必须位于6到12之间")
        private String username;
    
        @NotBlank(message = "密码不允许为空")
        private String password;
    
        @NotNull(message = "年龄不允许为空")
        private Integer age;
    
        @Pattern(regexp = "1[3,4,5,8]{1}\\d{9}",message = "请输入正确的手机号码")
        private String mobile;
    
        @Email(message = "请填写正确的邮箱地址")
        private String email;
    
        // @Valid 支持嵌套查询,会去检查 Test 类内部字段是否符合预期
        @Valid
        @NotNull(message = "测试数据不允许为空")
        private Test test;
    
    }
    
    /**
     * @author zb
     * @Description
     */
    @Data
    public class Test {
    
        @NotBlank(message = "uav不允许为空")
        private String uav;
    
    }
    
    
    • 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
    /**
     * @author zb
     * @Description
     */
    @Validated
    @Slf4j
    @RestController
    @RequestMapping("/api/v1")
    public class ValidationController {
    
        /**
         * 注解  功能
         * @AssertFalse 可以为null, 如果不为null的话必须为false
         * @AssertTrue 可以为null, 如果不为null的话必须为true
         * @DecimalMax 设置不能超过最大值
         * @DecimalMin 设置不能超过最小值
         * @Digits 设置必须是数字且数字整数的位数和小数的位数必须在指定范围内
         * @Future 日期必须在当前日期的未来
         * @Past 日期必须在当前日期的过去
         * @Max 最大不得超过此最大值
         * @Min 最大不得小于此最小值
         * @NotNull 不能为null,可以是空
         * @Null 必须为null
         * @Pattern 必须满足指定的正则表达式
         * @Size 集合、数组、map等的size()值必须在指定范围内
         * @Email 必须是email格式
         * @Length 长度必须在指定范围内
         * @NotBlank 字符串不能为null, 字符串trim()后也不能等于“”
         * @NotEmpty 不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于“”
         * @Range 值必须在指定范围内
         * @URL 必须是一个URL
         */
    
        /*
            @Valid是使用Hibernate validation的时候使用
            @Validated是只用Spring Validator校验机制使用
    
            说明:java的JSR303声明了@Valid这类接口,而Hibernate-validator对其进行了实现。
    
            @Validated与@Valid区别:
    
            [@Validated]:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上,[不支持嵌套检测]
            [@Valid]:可以用在方法、构造函数、方法参数和成员属性(字段)上,[支持嵌套检测]\
    
            public class LoginUser {
                @NotBlank(message = "手机号码不能为空")
                private String phone;
    
                // 【@Valid 支持 嵌套检查】
                @Valid
                @NotNull(message = "ageBean不能为null")
                private AgeBean ageBean;
            }
         */
    
        @PostMapping("/valid/test1")
        public Object get(@RequestBody @Validated Student student){
            System.out.println("student = " + student);
            return student;
        }
    
        @GetMapping(value = "/valid/test2")
        public String test2(@Validated Student student){
            // http://localhost:8090/api/v1/valid/test2?username=12345678&age=10&password=2342&test.uav=2
    
            // 当email  为空的时候,将不会纳入检查
            // 当mobile 为空的时候,将不会纳入检查
            log.info("Student is {}", student);
            return "test2 valid success";
        }
    
        /**
         * 注意,当使用单参数校验时需要在Controller上加上@Validated注解,否则不生效
         *
         * 当邮件为空的时候将不会检查
         */
        @PostMapping(value = "/valid/test3")
        public String test3(@Email(message = "这是一个不合法的邮件") String email){
            log.info("email is {}", email);
            return "email valid success";
        }
    
    }
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    验证分组验证

    /**
     * @author zb
     * @Description
     *
     *  id 和 appId 属性在新增操作时都是非必填,而在编辑操作时都为必填,
     *  name在新增操作时为必填
     */
    @Data
    public class StudentGroup {
    
        @Null(groups = ValidGroup.Crud.Create.class, message = "id必须为空")
        @NotBlank(groups = ValidGroup.Crud.Update.class, message = "id不能为空")
        private String id;
    
    //    @Null(groups = ValidGroup.Crud.Create.class, message = "appId必须为空")
        @NotBlank(groups = ValidGroup.Crud.Update.class, message = "appId不能为空")
        private String appId;
    
        @NotBlank(groups = ValidGroup.Crud.Create.class, message = "名称不允许为空")
        private String name;
    
        @Email(message = "请填写正确的邮箱地址")
        private String email;
    
    }
    
    /**
     * ValidGroup让其继承javax.validation.groups.Default,
     * 再在分组接口中定义出多个不同的操作类型,Create,Update,Query,Delete
     * @author zb
     */
    public interface ValidGroup extends Default {
      
        interface Crud extends ValidGroup{
    
            interface Create extends Crud{
    
            }
    
            interface Update extends Crud{
    
            }
    
            interface Query extends Crud{
    
            }
    
            interface Delete extends Crud{
    
            }
    
        }
    }
    
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    /**
     * @author zb
     * @Description 验证分组校验
     */
    @Validated
    @Slf4j
    @RestController
    @RequestMapping("/api/v1/group")
    public class ValidationGroupController {
    
        /**
         * 注解  功能
         * @AssertFalse 可以为null, 如果不为null的话必须为false
         * @AssertTrue 可以为null, 如果不为null的话必须为true
         * @DecimalMax 设置不能超过最大值
         * @DecimalMin 设置不能超过最小值
         * @Digits 设置必须是数字且数字整数的位数和小数的位数必须在指定范围内
         * @Future 日期必须在当前日期的未来
         * @Past 日期必须在当前日期的过去
         * @Max 最大不得超过此最大值
         * @Min 最大不得小于此最小值
         * @NotNull 不能为null,可以是空
         * @Null 必须为null
         * @Pattern 必须满足指定的正则表达式
         * @Size 集合、数组、map等的size()值必须在指定范围内
         * @Email 必须是email格式
         * @Length 长度必须在指定范围内
         * @NotBlank 字符串不能为null, 字符串trim()后也不能等于“”
         * @NotEmpty 不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于“”
         * @Range 值必须在指定范围内
         * @URL 必须是一个URL
         */
    
        @PostMapping(value = "/valid/add")
        public String add(@RequestBody @Validated(value = ValidGroup.Crud.Create.class) StudentGroup studentGroup){
            log.info("validEntity is {}", studentGroup);
            return "test3 valid success";
        }
    
    
        @PostMapping(value = "/valid/update")
        public String update(@RequestBody @Validated(value = ValidGroup.Crud.Update.class) StudentGroup studentGroup){
            log.info("validEntity is {}", studentGroup);
            return "test4 valid success";
        }
    
    }
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    自定义验证注解

    
    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.Documented;
    import java.lang.annotation.Repeatable;
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    import static java.lang.annotation.ElementType.*;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    /**
     * @author zb
     * @Description 自定义验证注解
     */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    @Repeatable(EnumString.List.class)
    @Documented
    @Constraint(validatedBy = EnumStringValidator.class)//标明由哪个类执行校验逻辑
    public @interface EnumString {
    
        String message() default "值不在枚举值中";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
        /**
         * @return 数据必须在这个值数组中
         */
        String[] value();
    
        /**
         * Defines several {@link EnumString} annotations on the same element.
         *
         * @see EnumString
         */
        @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
        @Retention(RUNTIME)
        @Documented
        @interface List {
    
            EnumString[] 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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * @author zb
     * @Description 自定义注解验证,实现逻辑
     *
     *      使用示例:
     *      @EnumString(value = {"F","M"}, message="性别只允许为F或M")
     *      private String sex;
     */
    public class EnumStringValidator implements ConstraintValidator<EnumString, String> {
    
        private List<String> enumStringList;
    
        @Override
        public void initialize(EnumString constraintAnnotation) {
            enumStringList = Arrays.asList(constraintAnnotation.value());
        }
    
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            if(value == null){
                return true;
            }
            return enumStringList.contains(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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
  • 相关阅读:
    第五届传智杯【初赛】- C-莲子的排版设计学
    C++中将string 类型与int类型的相互转换
    并查集(合并集合,连通块的点的数量)
    【21天Python进阶学习挑战赛】[day8]操作MySQL和SqlServer
    第06篇:池化技术
    C#NPOI操作Excel,实现Excel数据导入导出(支持多个sheet)
    在内网部署docker工程总结
    阿帽的小狗
    uoj#751-[UNR #6]神隐【交互】
    【SQL】数据库事务
  • 原文地址:https://blog.csdn.net/suprezheng/article/details/126329413