需求
新增接口
和 修改接口
中,手机号码
的格式校验是普遍需要的。
在每个手机号码
字段上添加正则表达式校验注解
来实现校验,重复书写,容易出错;
在不同的手机号码字段上,可能使用了不同的校验规则,无法有效统一校验规则。
目标
自定义一个用于校验手机号码格式
的注解@MobilePhone
,能够和现有的 Validation
兼容,使用方式和其他校验注解保持一致。
校验逻辑
有效格式
- 不能包含空格;
- 应为11位数字;
不校验非空
手机号码,校验的是格式;不校验是否为空(null 或 空字符串)。如果手机号码为空,直接通过校验;
这样设计是为了,将手机号码是否允许为空
,交给接口(业务逻辑)来决定;因为在不同的业务逻辑中,有时手机号码字段可以为空,有时不能为空。
但是,无论手机号码字段是否可以为空,只要客户端传递了手机号码,就应该保证格式是正确的。
Validation本身提供的@Email注解,也是这样的处理逻辑。
注:手机号码,使用字符串类型(Integer类型装不下11位的数值)。
核心代码
需要定义的内容包含两个部分:注解@MobilePhone
和 校验器PhoneValidator
。
注解:@MobilePhone
package com.example.core.validation.phone.strict;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 字符串必须是格式正确的手机号码。正确格式为:11位数字。
* <p>
* {@code null} 或 空字符串,是有效的(能够通过校验)。
* <p>
* 支持的类型:字符串
*
* @author songguanxun
* @since 1.0
*/
@Target({FIELD})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = MobilePhoneValidator.class)
public @interface MobilePhone {
/**
* @return the error message template
*/
String message() default "手机号码,格式错误";
/**
* @return the groups the constraint belongs to
*/
Class<?>[] groups() default {};
/**
* @return the payload associated to the constraint
*/
Class<? extends Payload>[] payload() default {};
/**
* 手机号码的详细描述。
* <p>
* 用于用户提示中,当页面中存在多个手机号码时,帮助用户更好的区分是哪个手机号码填错了。
*/
String description() default "手机号码";
}
校验器:PhoneValidator
package com.example.core.validation.phone.strict;
import com.example.core.constant.RegexConstant;
import com.example.core.validation.ResetMessageUtil;
import org.springframework.util.ObjectUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Pattern;
/**
* 手机号码格式校验器
*/
public class MobilePhoneValidator implements ConstraintValidator<MobilePhone, String> {
// 手机号码的详细描述。
private String description;
@Override
public void initialize(MobilePhone constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
description = constraintAnnotation.description();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (ObjectUtils.isEmpty(value)) {
return true;
}
if (value.contains(" ")) {
String message = String.format("%s,格式错误:不能包含空格", description);
ResetMessageUtil.reset(context, message);
return false;
}
if (!isMobilePhone(value)) {
String message = String.format("%s,格式错误", description);
ResetMessageUtil.reset(context, message);
return false;
}
return true;
}
private static final Pattern PATTERN = Pattern.compile(RegexConstant.MOBILE_PHONE);
/**
* 是手机号码
*/
private boolean isMobilePhone(CharSequence input) {
return PATTERN.matcher(input).matches();
}
}
使用
@MobilePhone
放在需要校验格式的 手机号码
字段上。
新增用户Param
package com.example.web.response.model.param;
import com.example.core.constant.RegexConstant;
import com.example.core.validation.idcard.IdCard;
import com.example.core.validation.phone.strict.MobilePhone;
import com.example.core.validation.zipcode.ZipCode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.util.Date;
@Data
@Schema(name = "新增用户Param")
public class UserAddParam {
// 其他字段 ...
@NotEmpty(message = "手机号码,不能为空")
@MobilePhone
@Schema(description = "手机号码", example = "18612345678", pattern = RegexConstant.MOBILE_PHONE)
private String mobilePhone;
}
编辑用户Param
package com.example.web.response.model.param;
import com.example.core.constant.RegexConstant;
import com.example.core.validation.phone.strict.MobilePhone;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.util.Date;
@Data
@Schema(name = "编辑用户Param")
public class UserEditParam {
// 其他字段 ...
@NotEmpty(message = "手机号码,不能为空")
@MobilePhone(description = "编辑用户-手机号码")
@Schema(description = "手机号码", example = "18612345678", pattern = RegexConstant.MOBILE_PHONE)
private String mobilePhone;
}
补充代码
ResetMessageUtil:重置提示信息工具类
package com.example.core.validation;
import javax.validation.ConstraintValidatorContext;
/**
* 参数校验 - 重置提示信息工具类
*/
public class ResetMessageUtil {
/**
* 重置提示信息
*/
public static void reset(ConstraintValidatorContext context, String message) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message).addConstraintViolation();
}
}
RegexConstant:正则表达式-常量
package com.example.core.constant;
/**
* 正则表达式-常量
*/
public class RegexConstant {
/**
* 全部为数字
*/
public static final String NUMBERS = "^\\d*$";
/**
* 邮政编码:6位数字
*/
public static final String ZIP_CODE = "^\\d{6}$";
/**
* 手机号码:11位数字
*/
public static final String MOBILE_PHONE = "1[3-9]\\d{9}$";
}