SpringBoot整合Hibernate Validator实现参数校验功能

1.引入Hibernate Validator依赖

<!-- 校验器 -->
<dependency>
   <groupId>org.hibernate.validator</groupId>
   <artifactId>hibernate-validator</artifactId>
</dependency>

2.创建校验配置类

Hibernate Validator有两种验证模式:
(1)普通模式(设置hibernate.validator.fail_fast为false)——会校验完所有的属性,然后返回所有的验证失败信息。
(2)快速模式(设置hibernate.validator.fail_fast为true)——校验过程中,只要有一个失败,则返回验证失败信息。

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

3.在实体类中成员变量中添加校验注解

常见的校验注解如下:

@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=)  被注释的元素必须符合指定的正则表达式         
@NotBlank(message =)   验证字符串非null,且长度必须大于0     
@Email  被注释的元素必须是电子邮箱地址     
@Length(min=,max=)  被注释的字符串的大小必须在指定的范围内     
@NotEmpty   被注释的字符串的必须非空     
@Range(min=,max=,message=)  被注释的元素必须在合适的范围内

实体类中部分校验注解配置如下:

@Data
public class UserModel implements Serializable{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1906142335911461001L;
	
	private String id;
	
	@NotNull(message = "姓名不能为空")
	@Length(min = 1, max = 20, message = "姓名为1到20个字符之间")
	@Pattern(regexp="^[\\u4e00-\\u9fa5]{0,}$", message="姓名为汉字字符")
	private String name;
	
	@Pattern(regexp="^[A-Za-z]+$", message="英文名称只能包含字母")
	@Length(min = 1, max = 20, message = "英文名称为1到20个字符之间")
	private String englishName;
	
	@Range(min = 1, max = 200, message = "年龄范围为1到200")
	private Integer age;
	
	@NotBlank(message="性别不能为空")
	@Pattern(regexp="^(0|1)$", message="性别不正确")
	private String gender; 
	
	@AssertFalse(message = "必须为false")
    private Boolean isFalse;
	
	@AssertTrue(message = "必须为true")
    private Boolean isTrue;

	@Range(min = 1, max = 500, message = "体重范围为1到500kg")
	private Float weight;
	
	@Range(min = 1, max = 300, message = "年龄范围为1到300cm")
	private Float tall;
	
	@NotNull(message = "身份证不能为空")
	@Pattern(regexp="^(\\d{18,18}|\\d{15,15}|(\\d{17,17}[x|X]))$", message="身份证格式错误")
	private String cardId;
	
	@Pattern(regexp="^((25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))\\.){3}(25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))$", message="ip格式不正确")
	private String ip;
	
	@Length(min = 0, max = 200, message = "工作内容不能超过200")
	@Pattern(regexp="^http[s]*://[^\\s]*$", message="个人网站地址格式不正确")
	private String website;
	
	@Length(min = 4, max = 20, message = "qq号码不能超过20")
	@Pattern(regexp="^[1-9][0-9]{4,}$", message="qq号是从1000开始")
	private String qqNum;
	
	@NotBlank(message="userName不能为空")
    @Length(max=6, min=3, message="userName最小3位,最大6位")
    private String userName;

    @NotBlank(message="密码不能为空")
    @Pattern(regexp="^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message="密码必须是8~10位数字和字母的组合")
    private String password;
    
    @Pattern(regexp="^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$", message="手机号格式不正确")
    private String cellphone;
    
    @Pattern(regexp="^\\d{3}-\\d{8}|\\d{4}-\\d{7}$", message="固定电话格式不正确")
    private String telephone;

    @Email(message="邮箱格式不正确")
    @Pattern(regexp="^([\\w-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([\\w-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$", message="邮箱格式不正确")
    private String email;
    
    @Past
    private Date bornDate;
    
    @Future
    private Date future;
	
    @Length(min = 0, max = 200, message = "地址不能超过200")
	private String address;
    
    @Pattern(regexp="^[1-9]\\d{5}(?!\\d)$", message="邮政编码格式不正确")
    private String postCode;

	@Valid
	private UserModel father;
	
	@Valid
	private UserModel mother;
	
	@Length(min = 0, max = 200, message = "工作内容不能超过200")
	private String work;
}

其中在“private UserModel father”上配置的“@Valid”作用是可以校验属性“father”对象内部的所有属性,即级联校验。
上述实体成员变量中有很多都使用了一个校验注解——@Pattern,在该注解上用户可以自定义正则表达式来实现字符串格式校验,本示例提供了汉字、英文、性别、ip、url、qq号码、手机号、固定电话、邮箱、邮政编码等模式。

4.在Controller中方法中添加@Valid注解,处理校验错误信息

一般对于校验错误信息处理有两种方式:
一种是在Controller中方法参数需要校验的实体上添加@Valid注解,方法再增加一个参数——“BindingResult result”,在方法中可以根据result对象获取校验信息,如下所示:

	@PostMapping("/testPost")
	public Object testPost(@RequestBody @Valid UserModel userModel, BindingResult result){  
        StringBuilder sb = new StringBuilder();
		if(result.hasErrors()){    
			for(ObjectError error:result.getAllErrors()){       
			   sb.append(error.getDefaultMessage()); 
			   sb.append(";"); 	
			} 	
             return sb.toString();   					    
		}  
		return userModel;
	}

另外一种是只需在Controller中方法参数需要校验的实体上添加@Valid注解:

	@PostMapping("/testPost2")
	public Object testPost2(@RequestBody @Valid UserModel userModel){  
		...
		return userModel;
	}

但需要在启动类所在包下创建控制器切面类,并创建方法捕捉MethodArgumentNotValidException异常,在该方法中统一处理校验失败信息:

@RestControllerAdvice
@Component
public class GlobalExceptionAdvice {
 
  @ExceptionHandler(value = MethodArgumentNotValidException.class)
  public WrappedResult validException(MethodArgumentNotValidException e) {
    //验证post请求的参数合法性
    MethodArgumentNotValidException notValidException = e;
    BindingResult result = notValidException.getBindingResult();
    StringBuilder sb = new StringBuilder();
    if(result.hasErrors()){    
		for(ObjectError error:result.getAllErrors()){  
			sb.append(error.getDefaultMessage()); 
			sb.append(";"); 
			
		} 						    
	}  
    String msg = sb.toString();
    //WrappedResult是自定义返回结果包装类,用户可以使用自己的工具类
    return WrappedResult.failedWrappedResult(msg);
  }
}

该方法主要的操作是从捕获的MethodArgumentNotValidException 异常对象中获取BindingResult 对象,再从BindingResult 对象中获取错误信息列表,遍历改列表,通过StringBuilder 对象拼接错误信息,最后通过WrappedResult包装成WrappedResult对象返回。

5.测试

本测试使用postman进行测试,首先启动示例工程,访问http://localhost:8080/testPost2,即测试使用切面控制器的校验处理方式:
测试数据:

{
	"id":"asfda546a6565565e6s568d79",
	"name":"张小三",
	"age":29,
	"cardId":"wererewrewttwwte545",
	"gender":"0",
	"isFalse":false,
	"isTrue":true,
	"weight":72.3,
	"tall":172,
	"ip":"192.168.1.101",
	"website":"http://www.zhangxiaosan.cn",
	"qqNum":"694434544",
	"userName":"tianyamingyuedao",
	"password":"sanquanbuliugen",
	"cellphone":"13845442123",
	"telephone":"010-43564234",
	"email":"2345435345@qq.com",
	"bornDate":"1992-07-17",
	"future":"2088-07-17",
	"address":"北京市西城区xxxx",
	"postCode":"100010",
	"work":"xxx公司",
	"father":{
		"id":"asfda546a6565565e6s568d79",
		"name":"张老三",
		"age":61,
		"cardId":"wererewrewttwwte545",
		"gender":"0",
		"isFalse":false,
		"isTrue":true,
		"weight":72.3,
		"tall":172,
		"ip":"192.168.1.101",
		"website":"http://www.张老三.cn",
		"qqNum":"694434544",
		"userName":"zhanglaosan",
		"password":"sanquanbuliugen",
		"cellphone":"13845442123",
		"telephone":"010-43564234",
		"email":"6944345@qq.com",
		"bornDate":"1960-07-17",
		"future":"2060-07-17",
		"address":"北京市西城区xxxx",
		"postCode":"100010"
	},
	"mother":{
		"id":"asfda546a6565565e6s568d79",
		"name":"陈小六",
		"age":57,
		"cardId":"wererewrewttwwte545",
		"gender":"1",
		"isFalse":false,
		"isTrue":true,
		"weight":72.3,
		"tall":172,
		"ip":"192.168.1.101",
		"website":"http://www.chenxiaoliu.cn",
		"qqNum":"694434544",
		"userName":"chenxiaoliu",
		"password":"sanquanbuliugen",
		"cellphone":"118245442123",
		"telephone":"010-43564234",
		"email":"6944345@qq.com",
		"bornDate":"1964-07-17",
		"future":"2060-07-17",
		"address":"北京市西城区xxxx",
		"postCode":"100010"
	}
}

(1)当校验配置类中设置hibernate.validator.fail_fast为true,即快速模式时,结果如下图:
快速模式测试结果
可以看到提示信息中只有一个提示——“身份证格式错误”。
(2)当校验配置类中设置hibernate.validator.fail_fast为false,即普通模式时,结果如下图:
普通模式测试结果
可以看到提示信息中有很多个提示,说明普通模式生效:还有一些一样的提示,因为测试数据中一些属性数据是的错误是一样的,说明级联校验成功。

6.高级用法

Hibernate Validator高级用法详见:
https://docs.jboss.org/hibernate/validator/6.1/reference/en-US/html_single/#validator-gettingstarted

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

mqfdyx

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

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

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

打赏作者

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

抵扣说明:

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

余额充值