springboot中hibernate validator校验模式,分组校验,自定义校验

检验模式

上面例子中一次性返回了所有验证不通过的集合,通常按顺序验证到第一个字段不符合验证要求时,就可以直接拒绝请求了。Hibernate Validator有以下两种验证模式:

  1. 普通模式(默认是这个模式):会校验完所有的属性,然后返回所有的验证失败信息。

  2. 快速失败模式:只要有一个验证失败,则返回。

通常在实际开发中,我们需要配置快速失败模式,快速失败模式的配置方式:

package com.morris.validator.config;

import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

@Configuration
public class ValidatorConfig {

    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                // 快速失败模式
                .failFast(true)
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }
}

也可以如下配置:

@Bean
public Validator validator() {
    ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
            .configure()
            // 快速失败模式
            //.failFast(true)
            .addProperty( "hibernate.validator.fail_fast", "true" )
            .buildValidatorFactory();
    return validatorFactory.getValidator();
}

参数校验

post参数校验

post参数校验需要在方法的参数前面加上注解 @Valid,然后就可以在方法参数对应的实体类的字段上面加上@NotNull、@NotEmpty注解进行校验了。

@PostMapping("add")
public R add(@Valid @RequestBody User user) {
    return R.ok();
}

get参数校验

get参数校验无法使用注解@Valid,需要使用@Validated注解来使得验证生效。

注入MethodValidationPostProcessor

在ValidatorConfig注入MethodValidationPostProcessor来开启@Validated注解的校验功能。

@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
    MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
    // 设置validator模式为快速失败返回
    postProcessor.setValidator(validator());
    return postProcessor;
}

Controller中的配置

Controller上面添加@Validated
注解,方法参数上面添加校验注解。

package com.morris.validator.controller;

import com.morris.validator.vo.R;
import com.morris.validator.vo.User;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;

@RestController
@RequestMapping("user")
@Validated
public class GetController {

    @PostMapping("get")
    public R add(@NotBlank(message = "status不能为空") String status) {
        return R.ok();
    }

}

ConstraintViolationException异常处理

对get参数校验会抛出ConstraintViolationException异常,所以需要在GlobalExceptionHandler中对ConstraintViolationException异常进行处理。

@ExceptionHandler(value = ConstraintViolationException.class)
public R ConstraintViolationExceptionHandler(ConstraintViolationException ex) {
    R r = null;
    Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
    Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
    while (iterator.hasNext()) {
        ConstraintViolation<?> cvl = iterator.next();
        r = R.fail(HttpStatus.BAD_REQUEST.value(), cvl.getMessageTemplate());
        break;
    }
    log.error("Custom exception! error={}", ex);
    return r;
}

测试

# curl -X POST http://localhost:8080/user/get -H "content-type:application/json"
{"code":400,"message":"status不能为空","result":null}

# curl -X POST http://localhost:8080/user/get?status= -H "content-type:application/json"
{"code":400,"message":"status不能为空","result":null}

# curl -X POST http://localhost:8080/user/get?status=xxxx -H "content-type:application/json"
{"code":200,"message":"OK","result":null}

级联校验

级联校验需要在校验的属性上面加上@Valid注解。

public class User {
... ...
    @Valid
    private Address address;
}

@Data
public class Address {

    @NotBlank(message = "province不能为空")
    private String province;

    private String city;
}

测试:

# curl -X POST http://localhost:8080/user/add -H "content-type:application/json" -d '{"userName":"xx", "age": 10, "address": {"province": "ShangHai"}}'
{"code":200,"message":"OK","result":null}

# curl -X POST http://localhost:8080/user/add -H "content-type:application/json" -d '{"userName":"xx", "age": 10, "address": {"province": ""}}'
{"code":400,"message":"province不能为空","result":null}

分组

Person类字段分组校验如下:

public interface PersonCreateGroup {
}

public interface PersonUpdateGroup {
}

@Data
public class Person {

    @NotNull(message = "id不能为空", groups = PersonUpdateGroup.class)
    private Integer id;

    @NotNull(message = "userName不能为空", groups = {PersonCreateGroup.class, PersonUpdateGroup.class})
    @NotBlank(message = "userName不能为空", groups = {PersonCreateGroup.class, PersonUpdateGroup.class})
    private String userName;

    @NotNull(message = "age不能为空")
    private Integer age;
}

Person类中三个分组分别对应的字段如下:

  • PersonUpdateGroup:id、userName

  • PersonCreateGroup:userName

  • Default:age

PersonController代码如下:

package com.morris.validator.controller;

import com.morris.validator.vo.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
@RequestMapping("person")
@Validated
public class PersonController {

    @PostMapping("add")
    @Validated(PersonCreateGroup.class)
    public R add(@Valid @RequestBody Person person) {
        return R.ok();
    }

    @PostMapping("update")
    @Validated(PersonUpdateGroup.class)
    public R update(@Valid @RequestBody Person person) {
        return R.ok();
    }

    @PostMapping("add2")
    public R add2(@Valid @RequestBody Person person) {
        return R.ok();
    }

}

下面分别对PersonController中的各个接口进行测试:

add:

# curl -X POST http://localhost:8080/person/add -H "content-type:application/json" -d '{"userName":"xxx", "age": 10}'
{"code":200,"message":"OK","result":null}

# curl -X POST http://localhost:8080/person/add -H "content-type:application/json" -d '{"userName":"xxx"}'
{"code":400,"message":"age不能为空","result":null}

# curl -X POST http://localhost:8080/person/add -H "content-type:application/json" -d '{"age":10}'
{"code":400,"message":"userName不能为空","result":null}

update:

# curl -X POST http://localhost:8080/person/update -H "content-type:application/json" -d '{"id":1, "userName":"xxx", "age": 10}'
{"code":200,"message":"OK","result":null}

# curl -X POST http://localhost:8080/person/update -H "content-type:application/json" -d '{"userName":"xxx", "age": 10}'
{"code":400,"message":"id不能为空","result":null}

# curl -X POST http://localhost:8080/person/update -H "content-type:application/json" -d '{"id":1,  "age": 10}'
{"code":400,"message":"userName不能为空","result":null}

# curl -X POST http://localhost:8080/person/update -H "content-type:application/json" -d '{"id":1, "userName":"xxx"}'
{"code":400,"message":"age不能为空","result":null}

add2:

# curl -X POST http://localhost:8080/person/add2 -H "content-type:application/json" -d '{"age": 10}'
{"code":200,"message":"OK","result":null}

# curl -X POST http://localhost:8080/person/add2 -H "content-type:application/json" -d '{}'
{"code":400,"message":"age不能为空","result":null}

总结:

  • 校验时指定了group,就会校验指定group和默认group

  • 校验时没指定group,只会校验默认group

校验时可以指定多个分组,并为这些分组指定顺序:

@GroupSequence({OneGroup.class, TwoGroup.class, ThreeGroup.class})
public interface PersonOrder {
}

自定义校验器

一般情况,内置的校验器可以解决很多问题。但也有无法满足情况的时候,此时,我们可以实现validator的接口,自定义自己需要的验证器。

如下所示,实现了一个身份证规则验证器:

package com.morris.validator.validator;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class IdCardValidator implements ConstraintValidator<IdCard, String> {

    /**
     * 身份证规则校验正则表达式
     */
    private String reg = "^(\\d{6})(\\d{4})(\\d{2})(\\d{2})(\\d{3})([0-9]|X)$";
    private Pattern pt = Pattern.compile(reg);

    @Override
    public void initialize(IdCard idCard) {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext arg1) {
        if (value == null) {
            return true;
        }
        Matcher m = pt.matcher(value);
        if (m.find()) {
            return true;
        }
        return false;
    }

}

自定义注解:

package com.morris.validator.validator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy= IdCardValidator.class)
public @interface IdCard {

    String message() default "身份证号码格式不对";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

使用如下:

public class Person {

... ...
    @IdCard(message = "身份证号码格式不对")
    private String idCardNo;
}

测试如下:

# curl -X POST http://localhost:8080/person/add -H "content-type:application/json" -d '{"userName":"morris","age":10, "idCardNo":"33"}'
{"code":400,"message":"身份证号码格式不对","result":null}

参考文档

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

morris131

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值