• JAVA里自定义注解来进行数据验证


    API开发中经常会遇到一些对请求数据进行验证的情况,这时候如果使用注解就有两个好处,一是验证逻辑和业务逻辑分离,代码清晰,二是验证逻辑可以轻松复用,只需要在要验证的地方加上注解就可以。

    Java提供了一些基本的验证注解,比如@NotNull@Size,但是更多情况下需要自定义验证逻辑,这时候就可以自己实现一个验证注解,方法很简单,仅需要两个东西:

    • 一个自定义的注解,并且指定验证器
    • 一个验证器的实现

    自定义验证注解

    考虑有一个API,接收一个Student对象,并希望对象里的age域的值是奇数,这时候就可以创建以下注解:

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = AgeValidator.class)
    public @interface Odd {
        String message() default "Age Must Be Odd";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    

    其中:

    • @Target指明这个注解要作用在什么地方,可以是对象、域、构造器等,因为要作用在age域上,因此这里选择FIELD
    • @Retention指明了注解的生命周期,可以有SOURCE(仅保存在源码中,会被编译器丢弃),CLASS(在class文件中可用,会被VM丢弃)以及RUNTIME(在运行期也被保留),这里选择了生命周期最长的RUNTIME
    • @Constraint是最关键的,它表示这个注解是一个验证注解,并且指定了一个实现验证逻辑的验证器
    • message()指明了验证失败后返回的消息,此方法为@Constraint要求
    • groups()payload()也为@Constraint要求,可默认为空,详细用途可以查看@Constraint文档

    创建验证器

    有了注解之后,就需要一个验证器来实现验证逻辑:

    public class AgeValidator implements ConstraintValidator<Odd,Integer> {
        @Override
        public void initialize(Odd constraintAnnotation) {
        }
    
        @Override
        public boolean isValid(Integer age, ConstraintValidatorContext constraintValidatorContext) {
            return age % 2 != 0;
        }
    }
    

    其中:

    • 验证器有两个类型参数,第一个是所属的注解,第二个是注解作用地方的类型,这里因为作用在age上,因此这里用了Integer
    • initialize()可以在验证开始前调用注解里的方法,从而获取到一些注解里的参数,这里用不到
    • isValid()就是判断是否合法的地方

    应用注解

    注解和验证器创建好之后,就可以使用注解了:

    public class Student {
        @Odd
        private int age;
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    @RestController
    public class StudentResource {
        @PostMapping("/student")
        public String addStudent(@Valid @RequestBody Student student) {
            return "Student Created";
        }
    }
    

    在需要启用验证的地方加上@Valid注解,这时候如果请求里的Student年龄不是奇数,就会得到一个400响应:

    {
        "timestamp": "2018-08-15T17:01:44.598+0000",
        "status": 400,
        "error": "Bad Request",
        "errors": [
            {
                "codes": [
                    "Odd.student.age",
                    "Odd.age",
                    "Odd.int",
                    "Odd"
                ],
                "arguments": [
                    {
                        "codes": [
                            "student.age",
                            "age"
                        ],
                        "arguments": null,
                        "defaultMessage": "age",
                        "code": "age"
                    }
                ],
                "defaultMessage": "Age Must Be Odd",
                "objectName": "student",
                "field": "age",
                "rejectedValue": 12,
                "bindingFailure": false,
                "code": "Odd"
            }
        ],
        "message": "Validation failed for object='student'. Error count: 1",
        "path": "/student"
    }
    

    也可以手动来处理错误,加上一个BindingResult来接收验证结果即可:

    @RestController
    public class StudentResource {
        @PostMapping("/student")
        public String addStudent(@Valid @RequestBody Student student, BindingResult validateResult) {
            if (validateResult.hasErrors()) {
                return validateResult.getAllErrors().get(0).getDefaultMessage();
            }
            return "Student Created";
        }
    }
    

    这时候如果验证出错,便只会返回一个状态为200,内容为Age Must Be Odd的响应。

  • 相关阅读:
    HDOJ 450题留念
    有关VIM的一些笔记
    hdu 2715
    POJ 1004
    链表的创建,添加结点,打印...
    C++ 静态数据成员小谈
    自定义String类
    sizeof/strlen小论
    多态之重载多态运算符重载那些事
    01背包问题
  • 原文地址:https://www.cnblogs.com/xz816111/p/9484902.html
Copyright © 2020-2023  润新知