如何使用Bean|如何使用Bean Validation 解决业务中参数校验

前言 在开发中经常需要写一些字段校验的代码,比如字段非空,字段长度限制,邮箱格式验证等等,写这些与业务逻辑关系不大的代码个人感觉有点麻烦:
验证代码繁琐,重复劳动
方法内代码显得冗长
每次要看哪些参数验证是否完整,需要去翻阅验证逻辑代码
叙述 Bean Validation是一个通过配置注解来验证参数的框架,它包含两部分Bean Validation API和Hibernate Validator。
Bean Validation API是Java定义的一个验证参数的规范。
Hibernate Validator是Bean Validation API的一个实现。
@Valid和Validated的比较
Spring Validation验证框架对参数的验证机制提供了@Validated(Spring's JSR-303规范,是标准JSR-303的一个变种),javax提供了@Valid(标准JSR-303规范),配合BindingResult可以直接提供参数验证结果。
@Valid : 没有分组功能,可以用在方法、构造函数、方法参数和成员属性(field)上,如果一个待验证的pojo类,其中还包含了待验证的对象,需要在待验证对象上注解@valid,才能验证待验证对象中的成员属性
@Validated :提供分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制,用在类型、方法和方法参数上。但不能用于成员属性(field)。
两者都可以用在方法入参上,但都无法单独提供嵌套验证功能,都能配合嵌套验证注解@Valid进行嵌套验证。
嵌套验证示例:

public class ClassRoom{@NotNullString name; @Valid// 嵌套校验,校验参数内部的属性@NotNullStudent student; }

@GetMapping("/room")// 此处可使用 @Valid 或 @Validated, 将会进行嵌套校验public String validator(@Validated ClassRoom classRoom, BindingResult result) {if (result.hasErrors()) {return result.getFieldError().getDefaultMessage(); }return "ok"; }

BindingResult 的使用 BindingResult必须跟在被校验参数之后,若被校验参数之后没有BindingResult对象,将会抛出BindException。
@GetMapping("/room")public String validator(@Validated ClassRoom classRoom, BindingResult result) {if (result.hasErrors()) {return result.getFieldError().getDefaultMessage(); }return "ok"; }

不要使用 BindingResult 接收String等简单对象的错误信息。简单对象校验失败,会抛出 ConstraintViolationException。主要就是接不着,你要写也算是没关系…
// ? 错误用法,也没有特别的错,只是 result 是接不到值。@GetMapping("/room")@Validated// 启用校验public String validator(@NotNull String name, BindingResult result) {if (result.hasErrors()) {return result.getFieldError().getDefaultMessage(); }return "ok"; }

修改校验失败的提示信息
可以通过各个校验注解的message属性设置更友好的提示信息。
public class ClassRoom{@NotNull(message = "Classroom name must not be null")String name; @Valid@NotNullStudent student; }

@GetMapping("/room")@Validatedpublic String validator(ClassRoom classRoom, BindingResult result, @NotNull(message = "姓名不能为空") String name) {if (result.hasErrors()) {return result.getFieldError().getDefaultMessage(); }return "ok"; }

message属性配置国际化的消息也可以的,message中填写国际化消息的code,在抛出异常时根据code处理一下就好了。
@GetMapping("/room")@Validatedpublic String validator(@NotNull(message = "demo.message.notnull") String name) {if (result.hasErrors()) {return result.getFieldError().getDefaultMessage(); }return "ok"; }

// message_zh_CN.propertiesdemo.message.notnull=xxx消息不能为空// message_en_US.propertiesdemo.message.notnull=xxx message must no be null

hibernate-validator 的使用
1.引入pom
org.hibernatehibernate-validator5.3.1.Final

2.dto入参对象属性加入注解
@Datapublic class UserModel implements Serializable {private String id; @NotBlank(message = "用户名不能为空")private String name; @NotNull(message = "性别不能不填")private Byte gender; @NotNull(message = "年龄不能不填")@Min(value = https://www.it610.com/article/0,message ="年龄必须大于0岁")@Max(value = https://www.it610.com/article/150,message ="年龄必须小于150岁")private Integer age; @NotBlank(message = "手机号不能不填")private String telphone; private String registerMode; private String thirdPartyId; private String encrptPassward; }

方法一:3.controller方法入参加入校验(@Validated )
@GetMapping("/getUser")public String validator(@Validated UserModel userModel , BindingResult result) {if (result.hasErrors()) {return result.getFieldError().getDefaultMessage(); }return "ok"; }

方法二:3.自定义封装ValidatorImpl类
@Componentpublic class ValidatorImpl implements InitializingBean{private Validator validator; //实现校验方法并返回校验结果public ValidationResult validate(Object bean){finalValidationResult result=new ValidationResult(); Set> validate = validator.validate(bean); if (validate.size()>0) {result.setHasError(true); validate.forEach(constraintViolation->{String errMsg=constraintViolation.getMessage(); String propertyName=constraintViolation.getPropertyPath().toString(); result.getErrorMsgMap().put(propertyName,errMsg); }); }return result; }@Overridepublic void afterPropertiesSet() throws Exception {this.validator= Validation.buildDefaultValidatorFactory().getValidator(); }}

方法二:4.自定义封装ValidationResult 类
public class ValidationResult {public boolean hasError=false; private Map errorMsgMap=new HashMap<>(); //实现通用的通过格式化字符串信息获取错误结果的msg方法public String getErrMsg(){return StringUtils.join(errorMsgMap.values().toArray(),","); }public boolean isHasError() {return hasError; }public void setHasError(boolean hasError) {this.hasError = hasError; }public Map getErrorMsgMap() {return errorMsgMap; }public void setErrorMsgMap(Map errorMsgMap) {this.errorMsgMap = errorMsgMap; }}

5.controller方法入参加入校验
@Autowiredprivate ValidatorImpl validator; @Override@Transactional(rollbackFor = Exception.class)public void register(UserModel userModel) throws BusinessException {UserDo userDo=new UserDo(); if (userModel == null) {throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR); }//validate进行入参校验ValidationResult validate = validator.validate(userModel); if (validate.isHasError()){throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,validate.getErrMsg()); }}

Bean Validation 的约束
  • @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(value) 被注释的元素必须符合指定的正则表达式
Hibernate Validator 附加的约束
Hibernate Validator 附加的 constraint:
  • @Email 被注释的元素必须是电子邮箱地址
  • @Length 被注释的字符串的大小必须在指定的范围内
  • @NotEmpty 被注释的字符串的必须非空
  • @Range 被注释的元素必须在合适的范围内
【如何使用Bean|如何使用Bean Validation 解决业务中参数校验】以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

    推荐阅读