springboot接口在接收参数时,除了对参数的类型限制外,有时也需要针对参数进行一系列的校验,Validator为我们提供一些固定格式的校验,例如:email、blank、length等格式,前两天在搬砖的时候就遇到这样的需求,一个参数为IP,所以需要验证是否满足IP格式。
当然,IP格式校验不可能只是验证是否有三个“.”,需要验证IP每一位上是否在0-255之间,Validator没有提供这样的校验,我们可以来自定义Validator的注解来满足我们的需求。
打开Validator引用的jar包可以看到咱们熟悉的一些注解。
虽然源码没看过,不过照葫芦画瓢还不会么,注解找到了,那么再找一下注解的处理类不就完事了。
找到了ConstraintValidator接口,看了一下他的实现类,眼熟不。。。。照着实现类写一下,再注入到容器中,那么在执行其他验证时,就会执行咱们自定义的注解。
以@Max注解为例,查看其实现类
其实现类为一个抽像类,再往下看,他的子类扩展了他的一些功能,那么我们就照着这个抽像类来写就行。
定义注解
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
/**
* @Author: Seven
*/
@Target({ ElementType.METHOD ,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = IPValidator.class)
public @interface IpCheck {
String message() default "{com.seven.dto.IpCheck.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
注解处理类
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @Author: Seven
*/
@Component
public class IPValidator implements ConstraintValidator<IpCheck, CharSequence> {
private static final String regex = "(((\\d{1,2})|(1\\d{1,2})|(2[0-4]\\d)|(25[0-5]))\\.){3}((\\d{1,2})|(1\\d{1,2})|(2[0-4]\\d)|(25[0-5]))";
@Override
public void initialize(IpCheck constraintAnnotation) {
System.out.println("进初始化方法了");
}
@Override
public boolean isValid(CharSequence val, ConstraintValidatorContext constraintValidatorContext) {
//验证逻辑
if(StringUtils.isEmpty(ip)){
return true;
}
Pattern pattern = Pattern.compile(regex); // 编译正则表达式
Matcher matcher = pattern.matcher(ip); // 创建给定输入模式的匹配器
return matcher.matches();
}
}
记得加上@Compont 注解。
将注解加在接收参数的属性上。
发送请求测试:
如果没有引用Validator校验,那么 也可以使用AOP的环绕通知来实现校验
/**
* @Author: Seven
*/
@Aspect
@Component
public class IpPamterCheck {
@Around(value = "@annotation(com.seven.IpCheck)")
public Object test(ProceedingJoinPoint point) throws Throwable {
Object[] args = point.getArgs();
if(args == null ){
return point.proceed();
}
for (int i = 0; i < args.length; i++) {
Field[] declaredFields = args[i].getClass().getDeclaredFields();
for (int j = 0; j < declaredFields.length; j++) {
boolean ip = declaredFields[j].getName().equals("ip");
String name = "";
if(ip){
name = declaredFields[j].getName().substring(0,1).toUpperCase() + declaredFields[j].getName().substring(1);
// 获取方法
Method m = args[i].getClass().getMethod("get" + name);
// 调用getter方法获取属性值
String value = (String) m.invoke(args[i]);
System.out.println(value);
Boolean ipCheck = CheckUtil.IPCheck(value);
if(!ipCheck){
return ApiResult.error(40001,"IP format is error !");
}
}
}
}
return point.proceed();
}
}
如果你还有什么好的方法,可以评论告诉我下。。。并且上面有什么错误的地方,可以给我提出。。我尽快更改。。