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

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

Java提供了一些基本的验证注解,好比@NotNull@Size,可是更多状况下须要自定义验证逻辑,这时候就能够本身实现一个验证注解,方法很简单,仅须要两个东西:ide

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

自定义验证注解

考虑有一个API,接收一个Student对象,并但愿对象里的age域的值是奇数,这时候就能够建立如下注解:this

@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 {};
}

其中:code

  • @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来接收验证结果便可:get

@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的响应。编译器

相关文章
相关标签/搜索