前言

大家好,我是月夜枫,在平时的开发工作中,我们通常需要对接口进行参数格式验证。当参数个数较少(个数小于3)时,可以使用if ... else ...手动进行参数验证。当参数个数大于3个时,使用if ... else ...进行参数验证就会让代码显得臃肿,这个时候推荐使用注解来进行参数验证。

一、常用注解

下面列举一些常用的验证注解:

  1. @NotNull:值不能为null;

  2. @NotEmpty:字符串、集合或数组的值不能为空,即长度大于0;

  3. @NotBlank:字符串的值不能为空白,即不能只包含空格;

  4. @Size:字符串、集合或数组的大小是否在指定范围内;

  5. @Min:数值的最小值;

  6. @Max:数值的最大值;

  7. @DecimalMin:数值的最小值,可以包含小数;

  8. @DecimalMax:数值的最大值,可以包含小数;

  9. @Digits:数值是否符合指定的整数和小数位数;

  10. @Pattern:字符串是否匹配指定的正则表达式;

  11. @Email:字符串是否为有效的电子邮件地址;

  12. @AssertTrue:布尔值是否为true;

  13. @AssertFalse:布尔值是否为false;

  14. @Future:日期是否为将来的日期;

  15. @Past:日期是否为过去的日期;

二、实现示例

2.1 创建项目,添加依赖

本示例中使用的spring boot 版本为2.7.7

使用IDEA创建一个spring boot项目。在项目的pom.xml文件中添加如下依赖:

org.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-validation

2.2 创建示例实体类

创建一个User实体类,在实体类中需要对属性进行如下验证:

  • name– 用户姓名,不能为空;

  • password– 密码,不能为空,长度不能小于6;

  • age– 年龄,大于0小于150;

  • phone– 手机号,满足手机号格式;

User实体类具体代码如下:

importlombok.Data;importjavax.validation.constraints.*;@DatapublicclassUser{@NotBlank(message="用户姓名不能为空")privateStringname;@NotBlank(message="密码不能为空")@Size(min=6,message="密码长度不能少于6位")privateStringpassword;@Min(value=0,message="年龄不能小于0岁")@Max(value=150,message="年龄不应超过150岁")privateIntegerage;@Pattern(regexp="^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\d{8}$",message="手机号格式不正确")privateStringphone;}

2.3 创建控制器类

创建一个简单的控制器类,用于演示参数验证功能。

控制器代码如下:

importcn.ddcherry.springboot.demo.util.R;importcn.ddcherry.springboot.demo.entity.User;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestBody;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;importjavax.validation.Valid;@RestController@RequestMapping("/user")publicclassUserController{@PostMapping("/save")publicRsave(@Valid@RequestBodyUseruser){returnR.ok(user);}}

我们在参数user前添加@Valid注解,表示验证该参数。

其中R是封装的一个简易工具类,用于统一返回结果格式。代码如下:

importlombok.Data;importjava.io.Serializable;@DatapublicclassRimplementsSerializable{privateintcode;privatebooleansuccess;privateTdata;privateStringmsg;privateR(intcode,Tdata,Stringmsg){this.code=code;this.data=data;this.msg=msg;this.success=code==200;}publicstaticRok(Tdata){returnnewR(200,data,null);}publicstaticRerror(Stringmsg){returnnewR(500,null,msg);}}

2.4 定义全局异常处理类

全局异常处理类代码如下:

importcn.ddcherry.springboot.demo.util.R;importorg.springframework.validation.BindException;importorg.springframework.validation.BindingResult;importorg.springframework.web.bind.annotation.ExceptionHandler;importorg.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvicepublicclassGlobalExceptionHandler{@ExceptionHandler(BindException.class)publicRhandleError(BindExceptione){BindingResultbindingResult=e.getBindingResult();returnR.error(bindingResult.getFieldError().getDefaultMessage());}}

我们在全局异常处理类中使用ExceptionHandler捕获BindException异常,获取参数验证异常信息,最后返回统一的异常结果格式。

2.5 测试

在接口测试工具中测试接口。

以密码长度不足6位为例,返回的结果如下图所示:

2.6 小结

至此,我们就简单地讲述了Spring Boot项目使用@Valid注解进行参数验证的实现步骤。示例的验证逻辑流程如下图所示:

三、进阶

3.1 @Valid与@Validated的区别

用于参数校验的注解通常有两个:@Valid@Validated。它们的区别有如下几点:

区别@Valid@Validated
来源@Valid是Java标准注解@Validated是Spring框架定义的注解。
是否支持分组验证不支持支持
使用位置构造函数、方法、方法参数、成员属性类、方法、方法参数,不能用于成员属性
是否支持嵌套校验支持不支持

3.2 自定义验证注解

除了框架自带的注解,平时的工作中可能需要我们自定义验证注解处理特定的业务需求。这里汪小成将上面User类中的手机号格式验证改成使用自定义注解的验证方式。

3.2.1 定义注解

@Documented@Retention(RUNTIME)@Constraint(validatedBy={PhoneValidator.class})@Target({METHOD,FIELD,ANNOTATION_TYPE,CONSTRUCTOR,PARAMETER,TYPE_USE})public@interfacePhone{Stringmessage()default"手机号格式错误";ClasspublicclassPhoneValidatorimplementsConstraintValidator{privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(PhoneValidator.class);privatestaticfinalStringREGEX="^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\d{8}$";@OverridepublicbooleanisValid(Strings,ConstraintValidatorContextcontext){booleanresult=false;try{result=Pattern.matches(REGEX,s);}catch(Exceptione){LOGGER.error("验证手机号格式时发生异常,异常信息:",e);}returnresult;}}

3.2.3 使用注解

@DatapublicclassUser{//省略其它代码-//@Pattern(regexp="^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\d{8}$",message="手机号格式不正确")+@PhoneprivateStringphone;}

这样我们就成功地使用自定义注解@Phone验证手机号格式了。

使用自定义注解实现业务验证的一个比较大的优点是可以复用。所有需要进行手机号格式验证的属性,只需要添加上@Phone注解就可以了。如果后期我们需要修改手机号的验证规则,只需要修改PhoneValidator类中的验证逻辑,就可以作用于所有添加了@Phone注解的字段了。

好了,本文的技术部分就到这里啦。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力。

求一键三连:点赞、转发、在看。

我从清晨走过,也拥抱夜晚的星辰,人生没有捷径,你我皆平凡,你好,陌生人,一起共勉。