0


如何在Spring Boot中优雅地进行参数校验

1. 前言

在平时的开发工作中,我们通常需要对接口进行参数格式验证。当参数个数较少(个数小于3)时,可以使用

if ... else ...

手动进行参数验证。当参数个数大于3个时,使用

if ... else ...

进行参数验证就会让代码显得臃肿,这个时候推荐使用注解来进行参数验证。

在Java中,注解(Annotation)是一种代码标记,通常用于提供元数据,这些元数据可以被编译器或运行时环境使用。这些注解通常用于框架和库中,以实现更加灵活和可配置的代码。

2. 常用注解描述

  1. @NotNull- 描述:标记一个值不能为null。- 示例:publicclassUser{@NotNullprivateString name;// ... }
  2. @NotEmpty- 描述:标记一个集合(如List、Set等)不能为空。- 示例:publicclassUser{@NotEmptyprivateList<String> interests;// ... }
  3. @NotBlank- 描述:标记一个字符串不能为空白(即null、空字符串或只包含空格)。- 示例:publicclassUser{@NotBlankprivateString username;// ... }
  4. @Size- 描述:标记一个字符串或集合的大小必须在指定的范围内。- 示例:publicclassUser{@Size(min =2, max =50)privateString username;// ... }
  5. @Min@Max- 描述:标记一个数值必须在指定的最小值和最大值之间。- 示例:publicclassUser{@Min(18)@Max(60)privateint age;// ... }
  6. @DecimalMin@DecimalMax- 描述:标记一个浮点数或双精度数必须在指定的最小值和最大值之间。- 示例:publicclassUser{@DecimalMin("0.01")@DecimalMax("100.00")privatedouble discount;// ... }
  7. @Digits- 描述:标记一个整数或浮点数必须在指定的精度和总数值范围内。- 示例:publicclassUser{@Digits(integer =3, fraction =2)// 总长度为5,3位整数,2位小数。例如:"123.45" 是合法的。 privateBigDecimal amount;// ... }
  8. @Pattern- 描述:标记一个字符串必须匹配指定的正则表达式。通常用于验证输入格式。例如电子邮件地址、电话号码格式等。@Pattern注解在javax.validation.constraints包中。@Pattern(regexp = “^\w{5,}$”)表示长度在5-20之间,由字母、数字、下划线组成的字符串。@Pattern注解用于类字段上,例如用户密码字段。- 示例:publicclassUser{@Pattern(regexp ="^[a-zA-Z0-9]*$")privateString password;//... }
  9. @Email- 描述:标记一个字符串必须是一个有效的电子邮件地址。- 示例:@EmailprivateString emailAddress;
  10. @AssertTrue@AssertFalse- 描述:标记一个布尔值必须为true或false。- 示例:@AssertTrueprivateboolean isValid;@AssertFalseprivateboolean isNotValid;
  11. @Future- 描述:标记一个日期必须是在未来某个时间点之后。- 示例:@FutureprivateDate expiryDate;
  12. @Past- 描述:标记一个日期必须是在过去某个时间点之前。- 示例:@PastprivateDate purchaseDate;

这些注解通常与验证框架(如Hibernate Validator)一起使用,以在运行时验证对象的属性。

3. 注解使用

  1. 在项目的pom.xml文件中添加如下依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
  1. 在实体类中使用上述注解,代码如下:packagecom.yyqq.demo.entity;importlombok.Data;importjavax.validation.constraints.*;@DatapublicclassUser{@NotBlank(message ="用户姓名不能为空")privateString name;@NotBlank(message ="密码不能为空")@Size(min =6, message ="密码长度不能少于6位")privateString password;@Min(value =0, message ="年龄不能小于0岁")@Max(value =150, message ="年龄不应超过150岁")privateInteger age;@Pattern(regexp ="^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\d{8}$", message ="手机号格式不正确")privateString phone;}
  2. 控制器类使用验证,代码如下:importcom.yyqq.demo.util.Result;importcom.yyqq.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("/add")publicResultadd(@Valid@RequestBodyUser user){returnResult.success(user);}}
  3. Result是封装结果的一个类,用于返回统一的结果,代码如下:packagecom.yyqq.demo.util;importlombok.Data;importjava.io.Serializable;@DatapublicclassResult<T>implementsSerializable{privateint code;privateboolean success;privateT data;privateString msg;privateResult(int code,T data,String msg){this.code = code;this.data = data;this.msg = msg;this.success = code ==200;}publicstatic<T>Result<T>sucess(T data){returnnewResult<>(200, data,null);}publicstatic<T>Result<T>fail(String msg){returnnewResult<>(500,null, msg);}}
  4. 定义全局异常处理类,我们在全局异常处理类中使用ExceptionHandler捕获BindException异常,获取参数验证异常信息,最后返回统一的异常结果格式,代码如下:packagecom.yyqq.demo.util;importcom.yyqq.demo.util.Result;importorg.springframework.validation.BindException;importorg.springframework.validation.BindingResult;importorg.springframework.web.bind.annotation.ExceptionHandler;importorg.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvicepublicclassGlobalExceptionHandler{@ExceptionHandler(BindException.class)publicResulthandleError(BindException e){BindingResult bindingResult = e.getBindingResult();returnResult.fail(bindingResult.getFieldError().getDefaultMessage());}}

4. 使用分组验证

  1. 用于插入记录时的分组验证,代码如下:
packagecom.yyqq.demo.interceptor;importjavax.validation.groups.Default;publicinterfaceInsertextendsDefault{}
  1. 用于更新记录时的分组验证,代码如下:
packagecom.yyqq.demo.interceptor;importjavax.validation.groups.Default;publicinterfaceUpdateextendsDefault{}
  1. 在实体类中进行分组标记,代码如下:
packagecom.yyqq.demo.entity;importlombok.Data;importjavax.validation.constraints.*;@DatapublicclassUser{@NotBlank(groups ={Insert.class,Update.class})@NotBlank(message ="用户姓名不能为空")privateString name;@NotBlank(message ="密码不能为空")@Size(min =6, message ="密码长度不能少于6位")privateString password;@Min(value =0, message ="年龄不能小于0岁")@Max(value =150, message ="年龄不应超过150岁")privateInteger age;@NotBlank(groups ={Insert.class,Update.class})@Pattern(regexp ="^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\\d{8}$", message ="手机号格式不正确")privateString phone;}
  1. 控制器类使用分组验证
packagecom.yyqq.demo.controller;importcom.yyqq.demo.util.Result;importcom.yyqq.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("/add")publicResultadd(@Validated(Insert.class)@RequestBodyUser user){returnResult.success(user);}@PostMapping("/update")publicResultupdate(@Validated(Update.class)@RequestBodyUser user){returnResult.success(user);}}

5. 自定义验证注解

除了框架自带的注解,平时的工作中可能需要我们自定义验证注解处理特定的业务需求。

  1. 定义注解
packagecom.yyqq.demo.validate;importjavax.validation.Constraint;importjavax.validation.Payload;importjava.lang.annotation.Documented;importjava.lang.annotation.Retention;importjava.lang.annotation.Target;importstaticjava.lang.annotation.ElementType.*;importstaticjava.lang.annotation.RetentionPolicy.RUNTIME;@Documented@Retention(RUNTIME)@Constraint(validatedBy ={PhoneValidator.class})@Target({METHOD,FIELD,ANNOTATION_TYPE,CONSTRUCTOR,PARAMETER,TYPE_USE})public@interfacePhone{Stringmessage()default"手机号格式错误";Class<?>[]groups()default{};Class<?extendsPayload>[]payload()default{};}
  1. 定义验证器类
packagecom.yyqq.demo.validate;importjavax.validation.ConstraintValidatorContext;importjavax.validation.ConstraintValidator;importjava.util.regex.Pattern;publicclassPhoneValidatorimplementsConstraintValidator<Phone,String>{privatestaticfinalStringREGEX="^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\\d{8}$";@OverridepublicbooleanisValid(String s,ConstraintValidatorContext context){boolean result =false;try{
            result =Pattern.matches(REGEX, s);}catch(Exception e){System.out.println("验证手机号格式时发生异常,异常信息:"+ e);}return result;}}
  1. 实体类使用注解
packagecom.yyqq.demo.validate;publicclassUser{//其他属性...//  @Pattern(regexp = "^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\\d{8}$", message = "手机号格式不正确")@PhoneprivateString phone;}

6. @Valid与@Validated的区别

用于参数校验的注解通常有两个:

@Valid

@Validated

。它们的区别有如下几点:
区别@Valid@Validated来源

@Valid

是Java标准注解

@Validated

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


本文转载自: https://blog.csdn.net/a342874650/article/details/135170153
版权归原作者 孤蓬&听雨 所有, 如有侵权,请联系我们删除。

“如何在Spring Boot中优雅地进行参数校验”的评论:

还没有评论