ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

SpringBoot环境下Hibernate Validator参数校验

2021-02-23 16:35:40  阅读:213  来源: 互联网

标签:Hibernate SpringBoot userVO 校验 public Validator message password class


参数校验

在开发中经常需要做字段的参数校验,一般是长度,非空判断,以及一些手机号码和邮箱号的格式验证,而这些代码大多又是重复劳动且与业务逻辑没有太大关系

hibernate validator 提供了一套比较比较方便的参数校验实现方式

首先,先添加参数校验依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
 </dependency>

注:spring-boot-starter-web中已经添加了,引入了就不要再引入了

参数校验简单实现

先简单实现一个注册的参数校验的demo

  1. controller层(参数UserVO前面需要添加注解@Validated或者@Valid

    @RestController
    public class ParamValidController {
    
    	/**
    	 * @param userVO
    	 * @return
    	 */
    	@PostMapping("/register")
    	public String register(@RequestBody @Validated UserVO userVO) {
    		return "注册成功";
    	}
    }
    
  2. 实体类

    @Data
    public class UserVO {
    	@NotBlank(message = "用户名不能为空")
    	String username;
    	@NotBlank(message = "密码不能为空")
    	String password;
    	@NotNull(message = "性别不能为空")
    	@Range(min = 0, max = 1, message = "性别不合法")
    	Integer sex;
    	@Range(min = 18, message = "年龄不能小于18")
    	@NotNull(message = "年龄不能为空")
    	Integer age;
    }
    
  3. 使用postman工具发送错误格式json进行测试

{
    "username":"",
    "password":"",
    "sex":0,
    "age":16
}

控制台会打印拦截信息(证明参数有误,校验失败)

Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String com.fisher.valid.controller.ParamValidController.register(com.fisher.valid.vo.UserVO) with 3 errors: [Field error in object 'userVO' on field 'username': rejected value []; codes [NotBlank.userVO.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userVO.username,username]; arguments []; default message [username]]; default message [用户名不能为空]] [Field error in object 'userVO' on field 'age': rejected value [16]; codes [Range.userVO.age,Range.age,Range.java.lang.Integer,Range]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userVO.age,age]; arguments []; default message [age],9223372036854775807,18]; default message [年龄不能小于18]] [Field error in object 'userVO' on field 'password': rejected value []; codes [NotBlank.userVO.password,NotBlank.password,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userVO.password,password]; arguments []; default message [password]]; default message [密码不能为空]] ]

从上面控制台打印的错误信息中可以得出抛出了MethodArgumentNotValidException异常,在开发中我们不可能返回这么一大串异常信息给前端,所以需要对异常信息进行格式化,而且一般当发现第一个参数校验失败时就立即返回错误,不会进行接下来的校验

校验模式

从上面的错误信息可以得出,一次性返回了所有的校验错误信息,一般当第一个字段校验错误的时候就会直接返回错误了,不会进行接下来的校验,Hibernate Validator中正好也实现了这个功能,可参考官方文档

普通模式(默认)

会校验完所有的属性,如然后返回所有的错误信息

快速失败返回模式

只要有一个参数校验失败,则立即返回,官方提供了两种配置方式官方配置

  1. hibernate.validator.fail_fast:true 快速失败返回模式 false 普通模式
@configuration
public class ValidConfig{
	@Bean
	public Validator validator(){
	ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class)
                .configure()
                .addProperty( "hibernate.validator.fail_fast", "true" )
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();
        return validator;
	}
}
  1. failFast:true 快速失败返回模式 false 普通模式
@configuration
public class ValidConfig{
	@Bean
	public Validator validator(){
	ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class)
                .configure()
                .failFast(true)
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();
        return validator;
	}
}

选择其中一种配置成功后,再次使用postman发送请求,发现错误信息只返回了一个

Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String com.fisher.valid.controller.ParamValidController.register(com.fisher.valid.vo.UserVO): [Field error in object 'userVO' on field 'password': rejected value []; codes [NotBlank.userVO.password,NotBlank.password,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userVO.password,password]; arguments []; default message [password]]; default message [密码不能为空]] ]
  1. 对异常信息进行格式化返回(上面报错异常为MethodArgumentNotValidException)
@RestControllerAdvice
public class ParamValidExceptionHandler {

    /**
     * 处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常。
     * 此方法无法格式化get请求里面 封装的VO参数
     *
     * @param exception
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public String paramValidFailed(MethodArgumentNotValidException exception) {
        System.out.println("进入了1");
        BindingResult bindingResult = exception.getBindingResult();
        StringBuilder error = new StringBuilder();
        bindingResult.getAllErrors().forEach(e -> {
            error.append("[" + e.getDefaultMessage() + "],");
        });
        return error.substring(0, error.length() - 1).toString();
    }

此时发送请求返回的异常信息为

[密码不能为空]

hibernate参数校验的几种实现方式

get请求下校验没有封装的参数

  1. 需要重新配置校验是否快速返回
@Configuration
public class ValidConfig {

	/**
	 *
	 * @return 对单个参数进行验证时配置的快速失败返回
	 */
	@Bean
	public MethodValidationPostProcessor methodValidationPostProcessor() {
		MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();/**设置validator模式为快速失败返回*/
		postProcessor.setValidator(validator());
		return postProcessor;
	}

	/**
	 * 该配置设置验证失败快速返回 有一处校验失败就立马返回,只会显示一处错误
	 * @return
	 */
	@Bean
	public Validator validator() {
		ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
				.configure()
				.addProperty("hibernate.validator.fail_fast", "true")
				.buildValidatorFactory();
		Validator validator = validatorFactory.getValidator();
		return validator;
	}
}
  1. 在控制层类上添加@Validated注解
@RestController
@Validated
public class ParamValidController {
	 * @param userVO
	 * @return
	 */
	@GetMapping("/login")
	public String login(@RequestParam("username") @NotNull(message="用户名不合法")String username,@RequestParam("password") @NotNull(message="密码不合法")String password) {
		return "登录成功";
	}
}

访问上面接口时参数校验失败会报错

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.validation.ConstraintViolationException: login.username: 用户名不合法] with root cause
  1. 对异常信息就行格式化返回
@RestControllerAdvice
public class ParamValidExceptionHandler {

    /**
     * 单个参数校验时的异常拦截
     *
     * @param exception
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public String test(ConstraintViolationException exception) {
        System.out.println("进入了2");
        Set<ConstraintViolation<?>> constraintViolations = exception.getConstraintViolations();
        StringBuilder error = new StringBuilder();
        constraintViolations.forEach(e -> {
            error.append("[" + e.getMessage() + "],");
        });
        return error.substring(0, error.length() - 1).toString();
    }
}

get请求下校验封装后的对象

这是我遇到的坑,当我把get请求接口的参数用对象进行封装后再进行访问时,虽然能进行参数校验,但是不能格式化异常信息,最后发现之前的异常拦截器没有生效,访问这个接口会报错BindException,如下

@RestController
@Validated
public class ParamValidController {

	 * @param userVO
	 * @return
	 */
	@GetMapping("/login")
	public String login(@Validated UserVO userVO) {
		return "登录成功";
	}

报错信息

 Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'userVO' on field 'password': rejected value [null]; codes [NotBlank.userVO.password,NotBlank.password,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userVO.password,password]; arguments []; default message [password]]; default message [密码不能为空]]

这个时候需要对BindException进行拦截才能进行异常信息的格式化

@RestControllerAdvice
public class ParamValidExceptionHandler {
    /**
     * 处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常
     *
     * @param exception
     * @return
     */
    @ExceptionHandler(BindException.class)
    public String bindExceptionHandle(BindException exception) {
        System.out.println("进入了3");
        BindingResult bindingResult = exception.getBindingResult();
        StringBuilder error = new StringBuilder();
        bindingResult.getAllErrors().forEach(e -> {
            error.append("[" + e.getDefaultMessage() + "],");
        });
        return error.substring(0, error.length() - 1).toString();
    }
}

分组校验

  1. 编写实体类
@Data
public class UserVO {
	@NotBlank(message = "用户名不能为空", groups = {Register.class, Login.class})
	String username;
	@NotBlank(message = "密码不能为空", groups = {Register.class, Login.class})
	String password;
	@NotNull(message = "性别不能为空", groups = {Register.class})
	@Range(min = 0, max = 1, message = "性别不合法",groups = {Register.class})
	Integer sex;
	@Range(min = 18, message = "年龄不能小于18", groups = Register.class)
	@NotNull(message = "年龄不能为空", groups = {Register.class})
	Integer age;

	public interface Register {}

	public interface Login {}

}
  1. controller层在参数前面的@Validated添加对应的分组信息
@RestController
@Validated
public class ParamValidController {

	/**
	 * @param userVO
	 * @return
	 */
	@PostMapping("/register")
	public String register(@RequestBody @Validated(UserVO.Register.class) UserVO userVO) {
		return "注册成功";
	}

	 * @param userVO
	 * @return
	 */
	@GetMapping("/login")
	public String login(@Validated(UserVO.Login.class) UserVO userVO) {
		return "登录成功";
	}
}

常用的注解

@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=) 被注释的元素必须在合适的范围内

参考资料

https://www.cnblogs.com/mr-yang-localhost/p/7812038.html#_label3

标签:Hibernate,SpringBoot,userVO,校验,public,Validator,message,password,class
来源: https://www.cnblogs.com/fishercomes/p/14434425.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有