在后台校验参数是接收参数后的第一步,如果使用@RequestParam接收参数,逐个校验,Controller方法会过长 校验代码页非常冗余而且多个方法都需要校验。
而使用@Validated 进行校验,可以更为快捷的完成参数校验的工作,而且可以将参数校验放置到Bean钟,Ctrl代码简洁优雅,参数校验实现复用
Validate规范
JSR303/JSR349 HibernateValidation SpringValidation三者关系
- JSR303/JSR349是一项标准提供了一些校验规范,如@Null @NotNull @Pattern等,他们位于javax.validation.constraints.*;
- HibernateValidation 则是对于这个规范的一个实践(不同于ORM框架Hibernate),并提供了一些其他的校验注解,如@Email @Length @Range等,它们位于org.hibernate.validator.constraints.*
- SpringValidation则是封装了HibernateValidation,而且添加了自动校验,并将校验信息封装到特定的类中,它们位于import org.springframework.validation.*
@Validated 使用
Controller方法上使用
1 2 3 4 5 6 7 8 9 10
| @Controller @Validated public class TestController { @RequestMapping(value = "/test", method = RequestMethod.GET) public String testValidate(@RequestParam(value = "recordId", defaultValue = "") @NotEmpty(message = "记录ID不可以为空") @Pattern(regexp = "\\d{1,11}", message = "记录ID不符合规范") String recordId) { return "test"; }
|
Bean上使用
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
| public class UserSchoolGradeDTO {
private Integer id;
@NotEmpty(message = "用户ID不可以为空") @Pattern(regexp = "\\d{1,11}", message = "用户ID不符合规范") private String userId;
@Digits(integer = 3, fraction = 1) @Range(min = 0, max = 100) @NotNull private Float subjectScore; }
public ResultBO insertUserSchoolGrade(@Validated UserSchoolGradeDTO userSchoolGradeDTO, BindingResult bindingResult) {
if (bindingResult.hasErrors()) { return Results.fail(bindingResult.getFieldErrors().get(0).getDefaultMessage()); } if(bindingResult.hasErrors()){ for (FieldError fieldError : bindingResult.getFieldErrors()) { } } }
|
常用校验注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| JSR提供的校验注解: @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(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator提供的校验注解: @NotBlank(message =) 验证字符串非null,且长度必须大于0 @Email 被注释的元素必须是电子邮箱地址 @Length(min=,max=) 被注释的字符串的大小必须在指定的范围内 @NotEmpty 被注释的字符串的必须非空 @Range(min=,max=,message=) 被注释的元素必须在合适的范围内
|
分组校验
当使用Bean校验时,同样一个Bean可能在不同的fuction接收校验的函数不同,通过使用分组校验可以实现这个功能,而不用编写多个Bean.
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
| Class Foo{
@Min(value = 18,groups = {Adult.class}) @Max(value = 18,groups = {Minor.class}) private Integer age;
public interface Adult{}
public interface Minor{} }
@RequestMapping("/drink") public String drink(@Validated({Foo.Adult.class}) Foo foo, BindingResult bindingResult) { if(bindingResult.hasErrors()){ for (FieldError fieldError : bindingResult.getFieldErrors()) { } return "fail"; } return "success"; }
@RequestMapping("/live") public String live(@Validated Foo foo, BindingResult bindingResult) { if(bindingResult.hasErrors()){ for (FieldError fieldError : bindingResult.getFieldErrors()) { } return "fail"; } return "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
| @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) @Retention(RUNTIME) @Documented
@Constraint(validatedBy = {CannotHaveBlankValidator.class}) public @interface CannotHaveBlank {
String message() default "不能包含空格";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE}) @Retention(RUNTIME) @Documented @interface List { CannotHaveBlank[] 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
|
public class CannotHaveBlankValidator implements ConstraintValidator<CannotHaveBlank, String> {
@Override public void initialize(CannotHaveBlank constraintAnnotation) { }
@Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value != null && value.contains(" ")) { String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate(); System.out.println("default message :" + defaultConstraintMessageTemplate); context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation(); return false; } return true; } }
|
手动校验
Hibernate Validation校验
1 2 3 4 5 6 7 8 9 10 11 12 13
| Foo foo = new Foo(); foo.setAge(22); foo.setEmail("000");
ValidatorFactory vf = Validation.buildDefaultValidatorFactory(); Validator validator = vf.getValidator();
Set<ConstraintViolation<Foo>> set = validator.validate(foo); for (ConstraintViolation<Foo> constraintViolation : set) { System.out.println(constraintViolation.getMessage()); }
|
Spring Validation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
@Autowired Validator globalValidator; <1>
@RequestMapping("/validate") public String validate() { Foo foo = new Foo(); foo.setAge(22); foo.setEmail("000");
Set<ConstraintViolation<Foo>> set = globalValidator.validate(foo);<2> for (ConstraintViolation<Foo> constraintViolation : set) { System.out.println(constraintViolation.getMessage()); }
return "success"; }
|
全局校验异常Catch
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
| @ExceptionHandler @ResponseBody public Map handle(MethodArgumentNotValidException exception) { return exception.getBindingResult().getFieldErrors() .stream() .map(FieldError::getDefaultMessage) .map(message->new HashMap<String,String>(){{ put("code", "2"); put("message", message); }}) .collect(Collectors.toList()).get(0); }
@ExceptionHandler @ResponseBody public Map handle(ConstraintViolationException exception) { return exception.getConstraintViolations() .stream() .map(ConstraintViolation::getMessage) .map(message->new HashMap<String,String>(){{ put("code", "2"); put("message", message); }}) .collect(Collectors.toList()).get(0); }
|