在做项目的时候,对pojo和传入的参数进行校验,如果是代码编写,需要很多if来判断
其实可根据一些校验的注解来实现我们的参数校验,主要介绍一下常用的 javax.validation 这个仓库的使用,这里总结一下
1、导包在项目的pom.xml 文件夹中导入包
javax.validation validation-api1.1.0.Final
这个验证根据JSR 380规范,validation-api 依赖包含标准验证注解
2、使用这个包的注解主要有以下这么多,先简单说明(以版本 号2.0.2为标准),下面再分别介绍
// 表明注解可以使用在哪里 // 这里表示注解可以使用在 方法 属性 注解 构造方法 参数 type @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) //描述保留注解的各种策略,它们与元注解(@Retention)一起指定注释要保留多长时间 @Retention(RetentionPolicy.RUNTIME) //表明这个注解是由 javadoc记录的 @documented public @interface MyOwnAnnotation { }2.1 注解详细属性的介绍
查看上面所有注解的源码,关于注解中的字段,主要分为以下两种
2.1.1 不能自定义值的以下面的这些注解为代表
AssertFalse AssertTrue
PositiveOrZero Positive Negative NegativeOrZero
NotNull Null NotEmpty NotBlank
FutureOrPresent Future PastOrPresent Past
以 @AssertFalse为例,该注解的详细内容(源码)
@documented @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) // 这个表明可以重复使用, //例如在一个field使用这个注解 // 一般注解只能使用一次 //@Max(value = 11) //Long num; // 如果使用了这个@Repeatable标注,那就可以使用两次 //@Max(value = 11) //@Max(value = 22) //Long num; @Repeatable(AssertFalse.List.class) // 这个Constraint 指定验证类,如果不自定义指定,实现其默认的验证类 ,如果指定 // @Constraint(validatedBy = { MyValidator.class }) @Constraint(validatedBy = {}) public @interface AssertFalse { // 校验失败的信息,可以自定义 String message() default "{javax.validation.constraints.AssertFalse.message}"; // 这里主要进行将validator进行分类,不同的类group中会执行不同的validator *** 作 Class>[] groups() default {}; // 这个主要是指定bean,一般很少使用 Class extends Payload>[] payload() default {}; @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @documented public @interface List { AssertFalse[] value(); } }2.1.2 能自定义值的
其他的注解都是可以自定义一些值,作为校验的参照值
以 @Max为例
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(Max.List.class) @documented @Constraint(validatedBy = {}) public @interface Max { // 校验失败的信息,可以自定义 String message() default "{javax.validation.constraints.Max.message}"; // 这里主要进行将validator进行分类,不同的类group中会执行不同的validator *** 作 Class>[] groups() default {}; Class extends Payload>[] payload() default {}; // 与上面不同的就是多了个value,这个需要自定义参数传入的值与这个value值对比,也就是校验的参考值 long value(); @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @documented public @interface List { Max[] value(); } }2.1.3 groups属性的使用
这个属性的作用就是举个例子:例如我们在传参的时候,加入我们创建一个用户,那么我们再传入的参数是不会包含用户的id,而如果更新这个用户,我们就需要传入用户id,那么根据多种校验规则的时候,这个属性就起作用了。
如果我们在一个属性使用注解的时候,如果不指定groups的时候,其实默认是在Default 组别中
@NotNull(message = "姓名不能为空") private String name; 等同于 import javax.validation.groups.Default; @NotNull(message = "姓名不能为空" , groups = {Default.class}) private String name;
那么根据这个情况,如果我们想自定义组别的时候,我们就可以分为以下几个步骤:
- 定义组别
- 使用组别
(1)定义组别
还是这个例子:如果创建一个用户,不用传入用户的id,如果更新这个用户,我们就需要传入用户id
那么我们就定义两个组别,创建用户组别、更新用户组别(其实就是两个接口,里面不用有方法,但是要注意要继承Default接口,理由下面会说)
import javax.validation.groups.Default; public interface CreateUser extends Default { } import javax.validation.groups.Default; public interface UpdateUser extends Default{ }
(2)使用组别
1)首先在pojo中,我们需要使用组别,若属性在不同的组别有不同的校验方式,那么就特殊指定其需要校验的规则,如果不指定,就还是默认按照Default
这里需要注意:定义的组别最好要继承Default接口,不然当我们在指定规则的校验时候,那么不标注groups的属性就不再校验了,因为默认按照Default
@Data class User{ @NotNull(message = "用户id不能为空", groups = UpdateUser.class) private Integer id; @NotNull(message = "姓名不能为空" ,groups = {CreateUser.class, UpdateUser.class}) private String name; @NotNull(message = "性别不能为空") private String sex; @Max(value = 20 ,message = "最大长度为20") private String address; @Email(message = "不满足邮箱格式") private String email; @AssertTrue(message = "字段为true才能通过") private boolean isAuth; @NotBlank(message = "手机号不能为空") @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误") private String mobile; @Future(message = "时间在当前时间之后才可以通过") private Date date; }
2)在接口中使用特定的组别。
两个接:
- 创建user的时候,指定验证的组别是CreateUser
- 更新user的时候,指定验证的组别是UpdateUser
(当自定义的组别继承了Default,这里指定验证组别的,属性会根据指定的groups来进行校验,那么没有指定groups的都会校验,如果不继承Default,那没有自定义groups为这个组别的属性就不生效了)
@PostMapping("/users/update") public ResponseDTO updateUser(@RequestBody @Validated(UpdateUser.class) User user) { userService.updateById(userDTO); return ResponseDTO.success(); } @PostMapping("/users/save") public ResponseDTO saveUser(@RequestBody @Validated(CreateUser.class) User user) { userService.saveUser(userDTO); return ResponseDTO.success(); }3、使用 3.1 第一种方式,pojo作为传参的形式
- Pojo 定义验证规则
- 以pojo作为参数传参
(1)User pojo类 定义校验规则
@Data class User{ @NotNull(message = "姓名不能为空") private String name; @NotNull(message = "性别不能为空") private String sex; @Max(value = 20 ,message = "最大长度为20") private String address; @Email(message = "不满足邮箱格式") private String email; @AssertTrue(message = "字段为true才能通过") private boolean isAuth; @NotBlank(message = "手机号不能为空") @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误") private String mobile; @Future(message = "时间在当前时间之后才可以通过") private Date date; }
(2)让校验规则生效。在Controller类的时候,我们只需要利用 @Validated 注解来实现pojo的校验
@RequestMapping("users") public ResponseDTO saveUser( @RequestBody @Validated User user){ }
(3)捕捉验证异常。如果参数校验通过,那么就直接执行接口方法,但是如果失败了,我们如何进行处理
MethodArgumentNotValidException是springBoot中进行绑定参数校验时的异常,需要在springBoot中处理
其他需要处理 ConstraintViolationException异常进行处理
一般像异常捕捉的,可以自定义一个异常捕捉Handler,实现异常捕捉后的数据返回
import com.dto.ResponseDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DuplicateKeyException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.NoHandlerFoundException; import javax.validation.ConstraintViolationException; import javax.validation.ValidationException; @RestControllerAdvice public class GlobalExceptionHandler { private Logger logger = LoggerFactory.getLogger(getClass()); private static int DUPLICATE_KEY_CODE = 1001; private static int PARAM_FAIL_CODE = 1002; private static int VALIDATION_CODE = 1003; @ExceptionHandler(BizException.class) public ResponseDTO handleRRException(BizException e) { logger.error(e.getMessage(), e); return new ResponseDTO(e.getCode(), e.getMessage()); } @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseDTO handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { logger.error(e.getMessage(), e); return new ResponseDTO(PARAM_FAIL_CODE, e.getBindingResult().getFieldError().getDefaultMessage()); } @ExceptionHandler(ValidationException.class) public ResponseDTO handlevalidationException(ValidationException e) { logger.error(e.getMessage(), e); return new ResponseDTO(VALIDATION_CODE, e.getCause().getMessage()); } @ExceptionHandler(ConstraintViolationException.class) public ResponseDTO handleConstraintViolationException(ConstraintViolationException e) { logger.error(e.getMessage(), e); return new ResponseDTO(PARAM_FAIL_CODE, e.getMessage()); } @ExceptionHandler(NoHandlerFoundException.class) public ResponseDTO handlerNoFoundException(Exception e) { logger.error(e.getMessage(), e); return new ResponseDTO(404, "不好意思,路径不存在,请检查路径是否正确"); } @ExceptionHandler(Exception.class) public ResponseDTO handleException(Exception e) { logger.error(e.getMessage(), e); return new ResponseDTO(500, "不好意思,系统繁忙,请稍后再试"); } }3.2 第二种方式,restful风格
- Controller 上加上@Validated
- 参数上加注解
@RestController @RequestMapping("user/") @Validated public class UserController{ @RequestMapping("users) public ResponseDTO getUser(@RequestParam("userId") @NotNull(message = "用户id不能为空") Long userId){ } }4、自定义使用
如果我们想自定义一个验证的注解,那么需要怎么做呢?
- 定义一个注解
- 编写一个验证类
- 使用
**(1)**我们首先定义一个像上面的@Null 这样的注解
@documented // 这里标注该注解是使用在filed和参数上的 @Target({ElementType.PARAMETER, ElementType.FIELD}) // 运行时生效 @Retention(RetentionPolicy.RUNTIME) // 指定验证的类是哪个 MyValidator 就是第二步做的事情 @Constraint(validatedBy = MyValidator.class) public @interface MyValid { // 提供自定义异常的信息,可以指定默认值 String message() default "参数不合法"; // 分组,详细看 上面的介绍 Class>[] groups() default {}; // 针对bean的,使用不多 Class extends Payload>[] payload() default {}; }
(2)编写一个验证类
我们需要编写一个 实现 ConstraintValidator 类实现类,来指定我们的校验规则
如果不指定特定注解的情况下,直接使用
// 这个是Max的指定的验证规则源码 public abstract class AbstractMaxValidatorimplements ConstraintValidator { protected long maxValue; public AbstractMaxValidator() { } public void initialize(Max maxValue) { this.maxValue = maxValue.value(); } public boolean isValid(T value, ConstraintValidatorContext constraintValidatorContext) { if (value == null) { return true; } else { return this.compare(value) <= 0; } } protected abstract int compare(T var1); } // 自定义的验证类 public class MyValidator implements ConstraintValidator { @Override public void initialize(Annotation constraintAnnotation) { // 可以获取注解的值 ,一般写在该方法中 } @Override public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) { // 编写自己属性验证的规则,o 则是待验证的值 return false; } }
如果在指定特定注解的情况下,那么我们就可特定 注解
// 自定义的验证类 public class MyValidator implements ConstraintValidator{ @Override public void initialize(MyValid myValid) { // 可以获取注解的值 ,一般写在该方法中 } @Override public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) { // 编写自己属性验证的规则,o 则是待验证的值 return false; } }
(3)使用。就跟正常的那些注解一样使用即可。
@Data public Class Example{ @NotBlank(message = "姓名不能为空") @MyValid(message = "姓名有误,请核对后提交") private String name; }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)