美文网首页SpringJava WebJava学习笔记
springmvc参数校验-JSR303(Bean Valida

springmvc参数校验-JSR303(Bean Valida

作者: lialzm | 来源:发表于2017-04-06 15:30 被阅读890次

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

Bean Validation 中的 constraint

constraint.png

Hibernate Validator 附加的 constraint

constraint.png

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

所需要jar

<dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.2.4.Final</version>
</dependency>

基本使用

定义在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定义
<mvc:annotation-driven validator="validator">
</mvc:annotation-driven>

    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
        <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
        <!-- 如果不加默认到 使用classpath下的 ValidationMessages.properties -->
        <property name="validationMessageSource" ref="messageSource"/>
    </bean>
    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <!-- 在web环境中一定要定位到classpath 否则默认到当前web应用下找  -->
                <value>classpath:messages</value>
            </list>
        </property>
        <property name="useCodeAsDefaultMessage" value="false"/>
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="cacheSeconds" value="60"/>
    </bean>

定义在参数中

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<? extends Payload>[] payload() default {};

}

对应校验器

public class UserValidator implements ConstraintValidator<CheckUser, User> {


    @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<? extends Payload>[] 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<? extends Payload>[] payload() default {};
}

对应的Validator

public class StatusValidator implements ConstraintValidator<Status, String> {
    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

相关文章

网友评论

  • Joepis:@Validated 这个注解既可以用在类上,也可以直接用在方法的参数列表是么?
    lialzm:@Joepis 都可以放,但是如果要验证参数,类上必须要有此注解

本文标题:springmvc参数校验-JSR303(Bean Valida

本文链接:https://www.haomeiwen.com/subject/mcdqattx.html