8.0 KiB
在后端开发中,数据校验是确保数据正确性非常重要的一个环节。Jakarta Validation(以前是Bean Validation,JSR 380)提供了一套丰富的标准注解来校验数据,比如 @NotNull、@NotBlank等,想必小伙伴们已经不陌生了。
比如,前面小节开发的获取手机验证码接口中,就对入参的 phone 字段添加了 @NotBlank 字符串非空校验注解,如下图所示。然而,单单校验字符串非空是不够的,如果用户提交的手机号格式有问题呢,比如提交了非数字、或者数字不满 11 位等等,这类的校验是没有现成的校验注解以供使用的,这个时候,就需要自定义校验注解了。
1. 自定义校验规则
接下来,我们就来亲手实现一个手机号校验注解。编辑 xiaoha-common 公共模块,添加 /validator 包,用于统一放置校验注解相关代码。首先,创建 PhoneNumberValidator 自定义校验类:
package com.quanxiaoha.framework.common.validator;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
/**
* @author: 犬小哈
* @date: 2024/4/15 22:23
* @version: v1.0.0
* @description: TODO
**/
public class PhoneNumberValidator implements ConstraintValidator<PhoneNumber, String> {
@Override
public void initialize(PhoneNumber constraintAnnotation) {
// 这里进行一些初始化操作
}
@Override
public boolean isValid(String phoneNumber, ConstraintValidatorContext context) {
// 校验逻辑:正则表达式判断手机号是否为 11 位数字
return phoneNumber != null && phoneNumber.matches("\\d{11}");
}
}
解释一下:
PhoneNumberValidator是一个用于自定义校验注解@PhoneNumber的验证器类。它实现了ConstraintValidator接口,用于验证一个字符串是否符合特定的手机号格式。1. 实现
ConstraintValidator接口public class PhoneNumberValidator implements ConstraintValidator<PhoneNumber, String>这行代码表明
PhoneNumberValidator类实现了ConstraintValidator<PhoneNumber, String>接口。ConstraintValidator接口有两个泛型参数:
PhoneNumber:自定义注解类型。String:被校验的属性类型。2.
initialize方法@Override public void initialize(PhoneNumber constraintAnnotation) { }
initialize方法是用来执行初始化操作的。这个方法在校验器实例化后会被调用,通常用来读取注解中的参数来设置校验器的初始状态。在这里,我们没有任何初始化操作,所以方法体是空的。3.
isValid方法@Override public boolean isValid(String phoneNumber, ConstraintValidatorContext context) { return phoneNumber != null && phoneNumber.matches("\\d{11}"); }
isValid方法包含了实际的校验逻辑。它有两个参数:
phoneNumber:需要验证的字符串,即被注解的属性值。context:提供了一些校验的上下文信息,通常用来设置错误消息等。校验逻辑的详细解释如下:
phoneNumber != null:首先检查phoneNumber是否为null。如果为null,则返回false,表示无效。这里也可以选择返回true,具体取决于业务需求是否允许空值。phoneNumber.matches("\\d{11}"):如果phoneNumber不为null,接着使用正则表达式\\d{11}验证字符串是否为 11 位的数字。\\d表示匹配一个数字字符,{11}表示匹配前面的模式正好 11 次。因此,这个正则表达式确保字符串是一个长度为 11 的纯数字字符串。
2. 自定义注解
接着,创建自定义注解 @PhoneNumber:
如何创建自定义注解
@interface类型的类?上个项目中,就有很多小伙伴提问,如何通过 IDEA 创建
@interface类型的?在高版本的 IDEA 中,有 Annotation 类型可供选择,如下图所示。低版本中如果没有,也可以先创建一个class类,再手动将class关键字改成@interface即可。
代码如下:
package com.quanxiaoha.framework.common.validator;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
/**
* @author: 犬小哈
* @date: 2024/4/15 22:22
* @version: v1.0.0
* @description: 自定义手机号校验注解
**/
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneNumberValidator.class)
public @interface PhoneNumber {
String message() default "手机号格式不正确, 需为 11 位数字";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
解释一下注解的各个部分的作用:
1.
@Target@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER })
@Target注解用于指定自定义注解可以应用的 Java 元素类型。在@PhoneNumber中,@Target的参数包括以下几个元素类型:
ElementType.METHOD:可以应用于方法。ElementType.FIELD:可以应用于字段。ElementType.ANNOTATION_TYPE:可以应用于其他注解。ElementType.PARAMETER:可以应用于方法参数。这种组合使得
@PhoneNumber注解可以被广泛使用在方法、字段、注解和参数上。2.
@Retention@Retention(RetentionPolicy.RUNTIME)
@Retention注解用于指定自定义注解的保留策略。RetentionPolicy.RUNTIME表示该注解在运行时仍然可用(可以通过反射机制访问)。这对于校验注解非常重要,因为校验框架需要在运行时读取注解并执行相应的校验逻辑。3.
@Constraint@Constraint(validatedBy = PhoneNumberValidator.class)
@Constraint注解用于指定关联的验证器类。在@PhoneNumber中,validatedBy属性指向PhoneNumberValidator.class,即自定义注解@PhoneNumber使用PhoneNumberValidator类进行校验。4.
messageString message() default "手机号格式不正确, 需为 11 位数字";
message元素用于定义验证失败时的错误消息。在使用注解时可以覆盖默认消息。default关键字用于提供该元素的默认值。
3. 使用 @PhoneNumber 注解
自定义注解开发完毕后,我们为获取手机验证码接口的入参实体类中的 phone 字段,添加上此注解,代码如下:
package com.quanxiaoha.xiaohashu.auth.model.vo.verificationcode;
import com.quanxiaoha.framework.common.validator.PhoneNumber;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SendVerificationCodeReqVO {
@NotBlank(message = "手机号不能为空")
@PhoneNumber
private String phone;
}
3. 自测一波
最后,重启项目,来自测一波功能好不好使。重新请求接口,同时故意将 phone 手机号少写一位,点击发送:
可以看到,自定义的 @PhoneNumer 校验注解工作正常,服务端成功返回了手机号格式不正确,需为 11 位数字的默认提示~