@Valid用于校验,其作用与@Validata效果基本相同,今天主要就是解决一个参数需要匹配多个正则表达式来校验是否通过的情况。
例如说:前端传给后端一个电话号码,既可能是手机号,也有可能是座机号,这样的话我们在字段上写一个@Pattern,然后为期自定义一条正则表达式显然是不能满足需求的,而且代码也显得杂乱不利于代码的复用。可以将其抽取出来作为一个注解就可以完美解决。
这两个依赖缺一不可
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
需要做一些准备工作
/**
* 实体
*/
@Data
public class Contacts {
/**
* 联系人姓名
*/
@NotBlank(message = "联系人姓名不能为空")
private String contactsName;
/**
* 联系人手机/电话号
*/
@Phone
private String phoneNum;
}
/**
* 全局响应实体
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
/**
* 请求结果
*/
private Boolean flag;
/**
* 响应信息
*/
private String message;
/**
* 响应内容
*/
private Object result;
/**
* 有异常时返回结果
*/
public static Result myError(List<ObjectError> constraintViolations) {
Result result = new Result();
result.setFlag(false);
Set<String> setMessage = new HashSet<>();
for (ObjectError message : constraintViolations) {
setMessage.add(message.getDefaultMessage());
}
result.setMessage(setMessage.toString());
return result;
}
}
/**
* 全局异常处理
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 参数校验全局异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handlerValidator(MethodArgumentNotValidException e) {
BindingResult result = e.getBindingResult();
List<ObjectError> allErrors = result.getAllErrors();
return Result.myError(allErrors);
}
}
自定义一个电话号码校验的注解,注意上面@interface只是定义出来了这个注解,没有定义它的校验规则,所义需要重新写一个类实现 ConstraintValidator<A extends Annotation, T>用来校验
@Documented
@Target({ElementType.FIELD, ElementType.PARAMETER}) //定义可以在字段上使用
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = Phone.PhoneCheck.class) //指定校验规则的类
public @interface Phone {
String message() default "请输入正确的手机号码";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* 校验规则
*/
@Component
class PhoneCheck implements ConstraintValidator<Phone, String> {
@Override
public boolean isValid(String phone, ConstraintValidatorContext constraintValidatorContext) {
// 国内移动用户正则表达式
Pattern pattern = Pattern.compile("^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\\d{8}$");
if (pattern.matcher(phone).matches()) {
return true;
}
// 国内固定号码正则表达式
pattern = Pattern.compile("\\d{3}-\\d{8}|\\d{4}-\\d{7}");
if (pattern.matcher(phone).matches()) {
return true;
}
String template = constraintValidatorContext.getDefaultConstraintMessageTemplate();
System.out.println(template);
return pattern.matcher(phone).matches();
}
}
}
测试,如果在controller层使用的话需要加上@Valid注解
@RestController
@Slf4j
public class ContactsController {
@SneakyThrows
@PostMapping("/contacts")
public Result message(@Valid @RequestBody Contacts contacts) {
log.info(contacts.toString());
return new Result(true,"校验通过",contacts);
}
}
如果校验通过的话,参数不会有异常抛出则会返回一个校验通过的消息,反之则会被自定义的全局异常处理捕获到,返回响应的异常信息。
测试结果:
可以看到无论是固定电话还是移动电话都可以校验成功。
再来个失败的情况
两个校验都没有通过