• SpringBoot


    前言

    后台开发中对参数的校验是不可缺少的一个环节,为了解决如何优雅的对参数进行校验?

    • JSR303(Java Specification Requests)应运而生,JSR303 是JavaBean参数校验的标准。
    • Bean Validation 为 JavaBean 验证定义了相应的元数据模型和 API。
    • Hibernate validator 5 是 Bean Validation 1.1的实现。

    常见注解

    • Bean Validation中定义的注解:
    注解 详细信息
    @Null 被注释的元素必须为 null
    @NotNull 被注释的元素必须不为 null
    @AssertTrue 被注释的元素必须为 true
    @AssertFalse 被注释的元素必须为 false
    @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
    @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
    @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
    @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
    @Size(max, min) 被注释的元素的大小必须在指定的范围内
    @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
    @Past 被注释的元素必须是一个过去的日期
    @Future 被注释的元素必须是一个将来的日期
    @Pattern(value) 被注释的元素必须符合指定的正则表达式
    • Hibernate validator 在JSR303的基础上对校验注解进行了扩展,扩展注解如下:
    注解 详细信息
    @Email 被注释的元素必须是电子邮箱地址
    @Length 被注释的字符串的大小必须在指定的范围内
    @NotEmpty 被注释的字符串的必须非空
    @Range 被注释的元素必须在合适的范围内

    参数校验的应用

    依赖

    • 注:SpringBoot 2.3.0之后放弃了默认对javax.validation 的支持,需引入一下依赖,2.3.0之前spring-boot-starter-web中内置。
    <!-- validation -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    

    简单的参数校验示例

    • 要想开启参数校验,需要在类上标注@Validated注解
    • 控制器
     @PostMapping(value = "/test")
     public ValidOneEvt param(@RequestBody @Validated ValidOneEvt evt){
         return evt;
     }
    
    • 实体验证
    @Getter
    @Builder
    public class ValidOneEvt {
    
        @NotEmpty(message = "名称不能为空")
        private String name;
    
        private String sex;
        
    }
    
    • 测试结果

    在这里插入图片描述


    级联校验

    • 级联校验需要在校验的实体上添加@Valid。
    • 控制器
    @PostMapping(value = "/test")
    public ValidOneEvt param(@RequestBody @Validated ValidOneEvt evt){
        return evt;
    }
    
    • 实体验证
    @Getter
    @Builder
    public class ValidOneEvt {
    
        @NotEmpty(message = "名称不能为空")
        private String name;
    
        private String sex;
    
        @Valid
        private ValidTwoEvt validTwoEvt;
    
    }
    
    • 级联实体
    @Getter
    @Setter
    public class ValidTwoEvt {
    
        @Length(min = 2)
        private String name;
    
    }
    
    • 请求示例

    在这里插入图片描述

    • 校验结果

    在这里插入图片描述


    @Validated 与 @Valid

    两者具有相似性
    注解地方

    • @Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上。
    • @Validated:可以用在类型、方法和方法参数上,不能用在成员属性(字段)上。

    @Validated和@Valid在级联验证功能上的区别

    • @Valid:用在方法入参上无法单独提供级联验证功能。能够用在成员属性(字段)上,提示验证框架进行级联验证。

    • @Validated:用在方法入参上无法单独提供级联验证功能。不能用在成员属性(字段)上,也无法提示框架进行级联验证。能配合级联验证注解@Valid进行级联验证。

    • 总结: 通常使用@Validated, 级联验证使用@Valid。


    自定义校验注解

    自定义校验注解用于基础校验注解不能满足业务需求。

    • 自定义效验注解验证密码是否相等
    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.*;
    
    /**
     * @Description 自定义参数校验注解
     *      @Documented 注解中的注释加入文档
     *      @Retention 注解保留阶段 RetentionPolicy.RUNTIME 运行阶段
     *      @Target 作用目标,注解的使用范围 TYPE:用于描述类、接口(包括注解类型) 或enum声明
     *      @Constraint 将注解和注解关联类关联到一起
     * @author coisini
     * @date Aug 10, 2021
     * @Version 1.0
     */
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Constraint(validatedBy = PasswordValidator.class)
    public @interface PasswordEquals {
    
        int min() default 4;
    
        int max() default 6;
    
        String message() default "passwords are not equal";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
    }
    
    • 自定义校验注解关联类
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    
    /**
     * @Description 自定义校验注解关联类
     *      ConstraintValidator的第一个参数:注解的类型
     *      ConstraintValidator的第二个参数:自定义注解修饰的目标的类型
     * @author coisini
     * @date Aug 10, 2021
     * @Version 1.0
     */
    public class PasswordValidator implements ConstraintValidator<PasswordEquals, ValidEvt> {
    
        private int min;
        private int max;
    
        /**
         * 初始化获取注解参数
         * @param constraintAnnotation
         */
        @Override
        public void initialize(PasswordEquals constraintAnnotation) {
            this.min = constraintAnnotation.min();
            this.max = constraintAnnotation.max();
        }
    
        /**
         * 校验参数
         * @param value
         * @param context
         * @return
         */
        @Override
        public boolean isValid(ValidEvt value, ConstraintValidatorContext context) {
            String password1 = value.getPassword1();
            String password2 = value.getPassword2();
    
            return password1.equals(password2) && this.validLength(password1, password2);
        }
    
        /**
         * 校验密码长度
         * @param password1
         * @param password2
         * @return
         */
        private boolean validLength(String password1, String password2) {
            return password1.length() > min && password1.length() < max
                    && password2.length() > min && password2.length() < max;
        }
    
    }
    
    • 自定义注解校验类
    /**
     * @Description 自定义注解校验类
     * @author coisini
     * @date Aug 10, 2021
     * @Version 1.0
     */
    @Getter
    @Builder
    @PasswordEquals(min = 1, message = "Incorrect password length or passwords are not equal")
    public class ValidEvt {
    
        private String password1;
        private String password2;
    
    }
    
    • 参数验证异常统一处理
    @ControllerAdvice
    public class GlobalExceptionAdvice {
    	/**
         * 参数校验异常处理器
         * @return
         */
        @ExceptionHandler(MethodArgumentNotValidException.class)
        @ResponseBody
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        public UnifyMessage handleBeanValidation(HttpServletRequest request, MethodArgumentNotValidException e) {
            String method = request.getMethod();
            String requestUrl = request.getRequestURI();
            System.out.println(e);
    
            List<ObjectError> errors = e.getBindingResult().getAllErrors();
            String message = formatAllErrorMessages(errors);
    
            return new UnifyMessage(10001, message,method + " " + requestUrl);
        }
    
        /**
         * 自定义注解校验异常处理器
         * @param req
         * @param e
         * @return
         */
        @ExceptionHandler(ConstraintViolationException.class)
        @ResponseBody
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        public UnifyMessage handleConstrainException(HttpServletRequest req, ConstraintViolationException e){
            String method = req.getMethod();
            String requestUrl = req.getRequestURI();
            String message = e.getMessage();
            return new UnifyMessage(10001, message,method + " " + requestUrl);
        }
    
        /**
         * 异常消息拼接
         * @param errors
         * @return
         */
        private String formatAllErrorMessages(List<ObjectError> errors){
            StringBuffer errorMsg = new StringBuffer();
            errors.forEach(error ->
                    errorMsg.append(error.getDefaultMessage()).append(";")
            );
            return errorMsg.toString();
        }
    }
    
    • 统一消息返回
    /**
     * @Description 统一消息返回
     * @author coisini
     * @date Aug 9, 2021
     * @Version 1.0
     */
    public class UnifyMessage {
    
        private int code;
        private String message;
        private String requestUrl;
    
        public int getCode() {
            return code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public String getRequestUrl() {
            return requestUrl;
        }
    
        public UnifyMessage(int code, String message, String requestUrl) {
            this.code = code;
            this.message = message;
            this.requestUrl = requestUrl;
        }
    
    }
    
    • 测试类
    @PostMapping(value = "/test1")
    public ValidEvt test1(@RequestBody @Validated ValidEvt evt){
        return evt;
    }
    
    • 测试结果

    在这里插入图片描述

    在这里插入图片描述

    - End -
    梦想是咸鱼
    关注一下吧
    以上为本篇文章的主要内容,希望大家多提意见,如果喜欢记得点个推荐哦
    作者:Maggieq8324
    本文版权归作者和博客园共有,欢迎转载,转载时保留原作者和文章地址即可。
  • 相关阅读:
    nodejs实战的github地址,喜欢的你还等啥
    java初始化深度剖析
    第三篇之消息的收发
    第二篇之收发消息的封装
    微信公众号开发第一篇之基本开发环境的搭建
    微信开发调试工具
    微信公众号开发入门教程第一篇
    linux常见驱动修改
    微信硬件开发步骤
    Linux系统快速启动方案
  • 原文地址:https://www.cnblogs.com/maggieq8324/p/15129526.html
Copyright © 2020-2023  润新知