Java参数校验@Validated、@Valid介绍及使用详解
作者:墨鸦_Cormorant
概述
介绍及使用
描述:Javax.validation是 spring 集成自带的一个参数校验接口。可通过添加注解来设置校验条件。springboot框架创建web项目后,不需要再添加其他的依赖。
使用:在Controller上使用 @Valid 或 @Validated 注解 开启校验
public String test(@RequestBody @Valid MyRequest req){};
@Validated 和 @Valid 的异同
相同点:
- 在检验参数符合规范的功能上基本一致;
不同点:
- 提供者不同:
- validated 是Spring Validation验证框架对参数的验证机制;
- valid是 javax 提供的参数验证机制
- 作用域不同:
- validated :类,方法,参数
- valid:方法,字段,构造方法,参数,TYPE_US
注:TYPE_USE:在 Java 8 之前的版本中,只能允许在声明式前使用 Annotation。而在 Java 8 版本中,Annotation 可以被用在任何使用 Type 的地方,例如:初始化对象时 (new),对象类型转化时,使用 implements 表达式时,或者使用 throws 表达式时。
//初始化对象时 String myString = new @Valid String(); //对象类型转化时 myString = (@Valid String) str; //使用 implements 表达式时 class MyList<T> implements List<@Valid T> {...} //使用 throws 表达式时 public void validateValues() throws @Valid ValidationFailedException{...}
参数校验常用注解
除了前四个 @Null,@ NotNull,@ NotBlank,@NotEmpty外,其他所有的注解,传 null 时都会被当作有效处理
注解常用参数值:message(校验不通过反馈信息)
JSR303定义的基础校验类型:
注解 | 验证的数据类型 | 备注 |
---|---|---|
Null | 任意类型 | 参数值必须是 Null |
NotNull | 任意类型 | 参数值必须不是 Null |
NotBlank | 只能作用于字符串 | 字符串不能为 null,而且字符串长度必须大于0,至少包含一个非空字符串 |
NotEmpty | CharSequence Collection Map Array | 参数值不能为null,且不能为空 (字符串长度必须大于0,空字符串(“ ”)可以通过校验) |
Size(min,max ) | CharSequence Collection Map Array | 字符串:字符串长度必须在指定的范围内 Collection:集合大小必须在指定的范围内 Map:map的大小必须在指定的范围内 Array:数组长度必须在指定的范围内 |
Pattern(regexp) | 字符串类型 | 验证字符串是否符合正则表达式 |
Min(value) | 整型类型 | 参数值必须大于等于 最小值 |
Max(value) | 整型类型 | 参数值必须小于等于 最大值 |
DecimalMin(value) | 整型类型 | 参数值必须大于等于 最小值 |
DecimalMax(value) | 整型类型 | 参数值必须小于等于 最大值 |
Positive | 数字类型 | 参数值为正数 |
PositiveOrZero | 数字类型 | 参数值为正数或0 |
Negative | 数字类型 | 参数值为负数 |
NegativeOrZero | 数字类型 | 参数值为负数或0 |
Digits(integer,fraction) | 数字类型 | 参数值为数字,且最大长度不超过integer位,整数部分最高位不超过fraction位 |
AssertTrue | 布尔类型 | 参数值必须为 true |
AssertFalse | 布尔类型 | 参数值必须为 false |
Past | 时间类型(Date) | 参数值为时间,且必须小于 当前时间 |
PastOrPresent | 时间类型(Date) | 参数值为时间,且必须小于或等于 当前时间 |
Future | 时间类型(Date) | 参数值为时间,且必须大于 当前时间 |
FutureOrPresent | 时间类型(Date) | 参数值为时间,且必须大于或等于 当前日期 |
字符串类型 | 被注释的元素必须是电子邮箱地址 |
Hibernate Validator 中附加的 constraint :
注解 | 验证的数据类型 | 备注 |
---|---|---|
Length | 字符串类型 | 字符串的长度在min 和 max 之间 |
Range | 数字类型 字符串类型 | 数值或者字符串的值必须在 min 和 max 指定的范围内 |
Pattern注解校验 常用正则表达式
@Pattern(regexp = "^[1-9]]\\d*$", message = "XX参数值必须是正整数")
高阶使用
自定义分组校验
有时多个场景接口公用一个请求对象,不同业务场景对请求对象的参数校验需求不同,可以使用分组校验来解决
注意:
- 没有指定显示分组的被校验字段和校验注解,默认都是 Default 组(即 Default.class)
- 若自定义的分组接口未继承 Default 分组,且 @Validated(或 @Valid)注解未传参 Default.class,则只会校验请求对象中进行了显示分组的字段,不会校验默认分组(没有进行显示分组)的字段
- 自定义的分组接口不继承 Default 分组 + @Validated(或 @Valid)注解传参 {自定义分组接口.class, Default.class}
- = 自定义的分组接口继承 Default 分组 + @Validated(或 @Valid)注解只传参自定义分组接口
示例:
新建自定义分组校验接口
public interface Student { }
import javax.validation.groups.Default; public interface Teacher extends Default { }
新建请求对象
@Data public class UserDTO { @NotBlank(message = "不能没有名称") private String name; @NotBlank(message = "老师不能没有手机号", groups = Teacher.class) private String phone; @NotEmpty(message = "学生不能没有书", groups = Student.clas) @Size(min = 2, message = "学生必须有两本书", groups = Student.class) private List<String> bookNames; }
Controller
@RestController public class ValidatedController { /** * 测试 校验student分组+默认分组 */ @PostMapping("student") public UserDTO validatedStudent(@Validated(value = {Student.class, Default.class}) @RequestBody UserDTO userDTO) { return userDTO; } /** * 测试 校验student分组+默认分组 */ @PostMapping("teacher") public UserDTO validatedTeacher(@Validated(value = {Teacher.class}) @RequestBody UserDTO userDTO) { return userDTO; } /** * 测试 分组校验 default */ @PostMapping("default") public UserDTO validatedDefault(@Validated(value = {Default.class}) @RequestBody UserDTO userDTO) { return userDTO; } /** * 测试 分组校验 onlyStudent */ @PostMapping("onlyStudent") public UserDTO validatedOnlyStudent(@Validated(value = {Student.class}) @RequestBody UserDTO userDTO) { return userDTO; }
自定义校验注解
定义注解
@Documented //指定注解的处理类 @Constraint(validatedBy = {VersionValidatorHandler.class }) @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) @Retention(RUNTIME) public @interface ConstantVersion { String message() default "{constraint.default.const.message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; String value(); }
注解处理类
public class VersionValidatorHandler implements ConstraintValidator<Constant, String> { private String constant; @Override public void initialize(Constant constraintAnnotation) { //获取设置的字段值 this.constant = constraintAnnotation.value(); } @Override public boolean isValid(String value, ConstraintValidatorContext context) { //判断参数是否等于设置的字段值,返回结果 return constant.equals(value); } }
自定义注解使用
@ConstantVersion (message = "verson只能为1.0.0",value="1.0.0") String version;
Controller
@RestController public class TestController { @RequestMapping("/test") public String createUser(@Valid User user, BindingResult bindingResult){ if (bindingResult.hasErrors()){ return bindingResult.getFieldError().getDefaultMessage(); } return "success"; } }
嵌套检验
描述:当对象 Man 的字段 houses 包含 House 对象类型时,在检验 houses 字段时可以检验 House 对象的属性字段时,就称为嵌套检验
方案:在被检验的字段上添加 @Valid 注解就可以实现嵌套检验
示例如下:
- 在检验 Man 对象的 houses 字段时,在houses 字段上添加 @Valid 注解后,就可以检验 list 中的 House 的属性是否符合要求;
- 否则只会检验 houses 的集合大小是否大于1,不会校验集合中的 House 对象,比如 House 对象的 name 长度是否符合要求。
class Man{ @Valid @Size(min = 1) private List<House> houses; } class House{ @Length(min = 1,max = 10) private String name; }
拓展
异常处理
参数校验异常:MethodArgumentNotValidException
方式一:基于异常监听@ControllerAdvice(参考:https://www.cnblogs.com/gezi0815/p/13815397.html)
/** * 全局异常处理器 */ @Slf4j @ControllerAdvice public class GlobalExceptionHandler { /** * 异常处理 */ @ResponseBody @ExceptionHandler(value = Exception.class) public DataResult exceptionHandler(Exception e) { log.error("GlobalExceptionHandler.exceptionHandler , 异常信息",e); return DataResult.fail(e.getMessage()); } /** * 业务异常 */ @ResponseBody @ExceptionHandler(value = BplCommonException.class) public DataResult bplCommonExceptionHandler(BplCommonException e) { log.warn("",e); return DataResult.fail(e.getMessage()); } /** * 处理所有RequestBody注解参数验证异常 */ @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseBody public DataResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e){ log.warn(e.getMessage()); return DataResult.fail(e.getBindingResult().getAllErrors().get(0).getDefaultMessage()); } /** * 处理所有RequestParam注解数据验证异常 */ @ExceptionHandler(BindException.class) public DataResult handleBindException(BindException ex) { FieldError fieldError = ex.getBindingResult().getFieldError(); log.warn("必填校验异常:{}({})", fieldError.getDefaultMessage(),fieldError.getField()); return DataResult.fail(ex.getBindingResult().getAllErrors().get(0).getDefaultMessage()); } }
方式二:基于Handle或Filter(参考:https://blog.51cto.com/u_12012821/2511625)
if (e instanceof MethodArgumentNotValidException) { String errorMsg = ((MethodArgumentNotValidException) e) .getBindingResult() .getAllErrors() .stream() .map(DefaultMessageSourceResolvable::getDefaultMessage) .collect(Collectors.joining(",")); resp = R.builder() .code(ResultCodeEnum.BUSINESS_ERROR.getCode()) .message(errorMsg).success(false) .build(); }
到此这篇关于Java参数校验@Validated、@Valid使用详解的文章就介绍到这了,更多相关java参数校验内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!