springmvc参数校验-JSR303(Bean Validation)

什么是jsr303?
JSR 303 – Bean Validation 是一个数据验证的规范,2009 年 11 月确定最终方案.
Hibernate Validator 是 Bean Validation 的最佳实践

Bean Validation 中的 constraint

springmvc参数校验-JSR303(Bean Validation)_第1张图片
constraint.png

Hibernate Validator 附加的 constraint

springmvc参数校验-JSR303(Bean Validation)_第2张图片
constraint.png

一个constraint由annotation和相应的 constraint validator 组成,一个annotation可以对应多个constraint validator,
constraint 之间也可以互相组合生成更复杂的constraint,如果还是满足不了需求则可以自定义全新的constraint

所需要jar


            org.hibernate
            hibernate-validator
            5.2.4.Final

基本使用

定义在bean中

public class User implements Serializable {
    @NotBlank
    private String id;
    //省略get,set
}

controller部分代码

    @RequestMapping("/test")
    @ResponseBody
    public String test(@ModelAttribute @Valid User user, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            ObjectError objectError = bindingResult.getAllErrors().get(0);
            if (objectError instanceof FieldError) {
                FieldError fieldError = (FieldError) objectError;
                logger.info(fieldError.getField() + fieldError.getDefaultMessage());
            }
        }
        logger.info(user.toString());
        return "";
    }

当请求参数id为空时打印id不能为空

自定义消息错误消息

Hibernate Validator支持硬编码错误消息和资源文件定义错误消息

  1. 硬编码
@NotBlank(message = "用户id不能为空")
private String id;

controller层不变,请求打印id用户id不能为空,这前面的id是从error获取的字段名并不是错误消息

  1. 从资源文件获取
    在resources文件下新建messages.properties
    添加user.id.null=id不能为null,添加注解@NotBlank(message = "${user.id.null}"),刚刚定义的错误信息会取代系统的错误信息
    xml定义



    
        
        
        
    
    
        
            
                
                classpath:messages
            
        
        
        
        
    

定义在参数中

jsr303是对bean的校验,不支持对参数进行校验,因此jsr309规范产生了,spring提供了@Validated注解对方法参数进行校验
controller层代码

@Controller
//这个注解要放在class上
@Validated
public class TestController2 {
    private Logger logger = LoggerFactory.getLogger(getClass());

    @RequestMapping("/test2")
    @ResponseStatus(HttpStatus.OK)
    public String test2(@Email @RequestParam String email) {
        logger.info(email);
        return "";
    }
}

如果不满足格式则返回javax.validation.ConstraintViolationException异常,这就需要定义一个ControllerAdvice去处理

@ControllerAdvice
public class MyExceptionHandler {

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public String handleValidationException(ConstraintViolationException e){
        for(ConstraintViolation s:e.getConstraintViolations()){
            return s.getInvalidValue()+": "+s.getMessage();
        }
        return "请求参数不合法";
    }
}

定义在类上

当和前端交互时经常需要使用的对前端传递的用户id和数据库中的比对,看是否存在此id,这时候将校验放在类上会减少重复代码

定义注解

@Constraint(validatedBy = {UserValidator.class})
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckUser {

    String message() default "错误的用户id";

    Class[] groups() default {};

    Class[] payload() default {};

}

对应校验器

public class UserValidator implements ConstraintValidator {


    @Autowired
    UserService userService;

    public void initialize(CheckUser constraintAnnotation) {

    }

    public boolean isValid(User user, ConstraintValidatorContext context) {
        return userService.checkUser(user);
    }
}

注解的使用

@CheckUser
public class User implements Serializable {
}

constraint组合

定义注解

@Max(10000)
@Min(8000)
@Constraint(validatedBy = {})
@Documented
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Price {
    String message() default "错误的价格";

    Class[] groups() default {};

    Class[] payload() default {};
}

定制constraint

自定义Constraint需要一个Constraint注解和对应的Validator

@Constraint(validatedBy = {StatusValidator.class})
@Documented
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Status {
    String message() default "不正确的状态 , 应该是 'created', 'paid', shipped', closed'其中之一";

    Class[] groups() default {};

    Class[] payload() default {};
}

对应的Validator

public class StatusValidator implements ConstraintValidator {
    private final String[] ALL_STATUS = {"created", "paid", "shipped", "closed"};

    public void initialize(Status status) {

    }

    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (Arrays.asList(ALL_STATUS).contains(value)) {
            return true;
        }
        return false;
    }
}

使用的话就和普通的constraint是相同的

分组验证、分组顺序

当相同的bean同一个字段需要不同的校验规则时就需要分组验证

首先定义分组接口用于标识

public interface Group1 {
}

public interface Group2 {
}

在bean添加注解

 @Max(value = 50,groups = Group1.class)
 @Min(value = 18,groups = Group1.class)
 private Integer age;

controller层

    @RequestMapping("/testGroup")
    @ResponseBody
    public User testGroup(@ModelAttribute @Validated(value = Group1.class) User user,BindingResult bindingResult) {
        if (bindingResult.hasErrors()){
            logger.info("error");
            return null;
        }
        return user;
    }

多个组则使用value = {Group1.class, Group2.class}
这里会出现一个问题就是会校验每一个条件并返回,但是每次返回的错误顺序可能是不一样的,这时要使用@GroupSequence({Group1.class,Group2.class,User.class})来控制校验顺序,即先校验group1再校验group2

你可能感兴趣的:(springmvc参数校验-JSR303(Bean Validation))