最近项目上线了,就闲着在看博客了。然后看到一篇写了关于SpringMVC自定义验证的文章,写得挺好,然后发现自己把注解的一些知识点忘记了,既然学习回来了,那就写一个博客分享一下自己复(chao)习(xi)回来的知识吧。
这边也分享一下看到的写sprinmvc自定义验证的文章:https://www.baeldung.com/spring-mvc-custom-validator
本文基于springboot2.0.4.RELEASE
一、准备
先引入所需的依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
二、学习一下自定义注解的一些基础
package com.dai.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
//这里是static的一种用法,静态导包。
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Created by dh on 2018/10/23.
* @Retention:java.lang.annotation.RetentionPolicy->(SOURCE,CLASS,RUNTIME)注解的保留策略
* SOURCE:注解仅存在于源码中,在class字节码文件中不包含
* CLASS:默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
* RUNTIME:注解会在class字节码文件中存在,在运行时可以通过反射获取到
*
* @Target:java.lang.annotation.ElementType->注解的作用目标
* (TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,ANNOTATION_TYPE,PACKAGE,TYPE_PARAMETER,TYPE_USE)
* TYPE:接口、类、枚举、注解
* FIELD:属性、枚举的常量
* METHOD:方法
* PARAMETER:方法上的参数(形参)
* CONSTRUCTOR:构造函数
* LOCAL_VARIABLE:局部变量
* ANNOTATION_TYPE:注解
* PACKAGE:包
* @Documented注解包含在javadoc中
* @Inherited注解可以被继承
* @Constraint注解标注表明指定了一个用于验证的类
*
*/
@Documented
@Target({ANNOTATION_TYPE, METHOD, CONSTRUCTOR, FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = IsMobileValidator.class)
//在定义限制类型的注解时message、groups和payload属性是必须的(不然请求会返回500)
public @interface IsMobile {
//是否必须
boolean required() default false;
String message() default "手机号码格式出错";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@Constraint注解可以用validatedBy指定用于验证的类。且验证的类要求是Class<? extends ConstraintValidator<?, ?>>的。还需要注意的是validatedBy是接受数组的
然后来看一下用于验证的类,首先需要实现ConstraintValidator<A extends Annotation, T>接口。第一个参数是我们上面写的注解,然后第二个参数是需要验证的目标对象的类型,然后再实现一下initialize和isValid方法。
initalize方法如名字的意思就是在该验证器初始化的时候被调用的,我们可以在这里做一些事情,比如说在验证前判断是否手机号是必填的。isValid方法就是判断是否验证通过的标准,验证的详细操作可以在这个方法里面做具体的实现。
package com.dai.validator;
import com.dai.util.ValidatorUtil;
import org.springframework.util.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* Created by dh on 2018/10/23.
*/
public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {
private static boolean required = false;
@Override
public void initialize(IsMobile constraintAnnotation) {
required = constraintAnnotation.required();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (required) {
return ValidatorUtil.isMobile(value);
} else {
if (StringUtils.isEmpty(value)) {
//非必填的。可为空
return true;
} else {
//非必填,但是不空的情况下还是需要验证
return ValidatorUtil.isMobile(value);
}
}
}
}
package com.dai.util;
import org.apache.commons.lang3.StringUtils;
import java.util.regex.Pattern;
/**
* Created by dh on 2018/10/23.
*/
public class ValidatorUtil {
private static final Pattern REGEX_MOBILE = Pattern.compile("(13|14|15|17|18|19)[0-9]{9}");
public static boolean isMobile(String src) {
if (StringUtils.isEmpty(src)){
return false;
}
return REGEX_MOBILE.matcher(src).matches();
}
}
好了。上面就完成了自定义验证的注解方式了。接下来就为我们需要进行验证的参数加上该注解再测试一下就OK啦。这里我就不写页面表单去验证了,直接用postman开测啦!
package com.dai.entity;
import com.dai.validator.IsMobile;
import javax.validation.constraints.NotNull;
/**
* Created by dh on 2018/10/23.
*/
public class User {
@NotNull
@IsMobile(required = true)
private String mobile;
//此处省略了get、set方法
}
package com.dai.controller;
import com.dai.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.validation.Valid;
/**
* Created by dh on 2018/10/23.
*/
@Controller
@RequestMapping("/moblieV")
@ResponseBody
public class MobileTestController {
@PostMapping("/validator.do")
public String validatorMoblie(@Valid User user){
//简单.哈哈哈
return "success";
}
}
现在是输入正确的手机号码接口是能够正常返回的。在测试错误的手机号码之前我需要先说一下,就是在验证出现错误的时候是会抛出BindException的,所以这里我们还可以使用全局异常处理器来对这个异常进行处理(可以按照自己制定的返回参数返回信息给调用者)
package com.dai.exception;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* Created by dh on 2018/10/23.
*/
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
@ExceptionHandler(value = BindException.class)
public String exceptionHandler(HttpServletRequest request, Exception e) {
//这里我不做过多的操作哈,直接将返回的msg进行输出(可自行控制响应参数)
BindException ex = (BindException) e;
List<ObjectError> errors = ex.getAllErrors();
return errors.get(0).getDefaultMessage();
}
}
验证一下,是否能够被全局异常处理器处理并且返回该响应参数呢?我们想要的应该就是输出:手机号码格式出错
好了,如愿以偿的得到我们想要的结果,大功告成!!!这里的话我分享一个postman的小技巧哈,可以点击右上角的设置按钮,然后再点击Add按钮去添加一个environment(如下图所示哈)。然后填入你自己熟悉的名称,然后在链接栏可以用{{host}}来引用到我们刚才填写的那段http://localhost:8080了(在链接栏输入 { 的时候就会自动有提示弹出啦,很方便)。不过使用之前记得要在右上角选择environment哦,这样的设置在开发和测试环境的切换是很方便省事的。