引言
今天开会,说前端与后端数据交互使用restful形式,把请求数据放到Http请求的头部中。这样后台的数据校验就跟之前用json有一些不同。大家开发中应该也遇到过类似的问题,如果是json形式的请求参数可以直接封装到java bean中,用Valid注解跟bindResult去处理数据校验结果。 但如果只有一两个基本型参数,如String name, int age,封装到一个bean中反而觉得有点麻烦,那就需要用到手动数据校验。
请求参数在http请求头部中
比如前端是这样传来一个name跟age,
http://xxx.com/companies?name=xxx&age=
在controller中接收的参数如下,参数不是一个实体,那只能手动实现方法级别的数据校验。后面的步骤虽然费点功夫,但是很灵活。
@RequestMapping("doRegist")
@Valid
public String registHandler(@NotBlank @NotNull String name, @Min(value=1, message="age too young") String age)
手动校验的步骤
1. pom.xml中添加配置
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.17.Final</version>
</dependency>
2. Srping中的aop配置
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy />
3.AOP切入类
下面的例子就是上述controller中的name,跟age参数进行的手动数据校验。
该类通过添加@Aspect注册到AOP容器中。
- 通过JoinPoint类获得被切入的对象,跟方法,以及方法参数。
- JoinPoint#getThis()方法,可以获得上述controller的class对象
- 通过controller的class对象的反射,获得需要数据校验方法的参数
@Component
@Aspect
public class ValidationAspect {
Logger logger = LoggerFactory.getLogger(this.getClass());
// 定义切入点。这里是对包下所有的注解了@Valid的controller进行切入
@Pointcut("execution(* com.cuts.siled.controller..*.*(..))&& @annotation(javax.validation.Valid))")
public void cutPoint() {}
// controller执行前,切入
@Before(value = "cutPoint()")
public void startValidCheck(JoinPoint jp) throws NoSuchMethodException, SecurityException {
logger.debug("@Before: {}" , jp.getSignature());
Object[] values = new Object[jp.getArgs().length];
// 获得controller的class对象
Class<? extends Object> cls = jp.getThis().getClass();
// 通过反射,获得controller的参数,参数值
Class[] args = new Class[jp.getArgs().length];
for (int i = 0; i < jp.getArgs().length; i++) {
args[i] = jp.getArgs()[i].getClass();
values[i] = jp.getArgs()[i];
}
ValidUtil util = new ValidUtil();
HashMap<String , String> map = new HashMap<String, String>();
// 使用工具类进行校验,并把结果放在map中。具体怎么处理map中的结果,根据业务
boolean normal = util.validateMethod(jp.getThis(), cls.getMethod(jp.getSignature().getName(), args), values, map);
if (!normal) {
for (String key : map.keySet()) {
logger.error("key:{},value:{}", key, map.get(key));
}
throw new ValidationException("validation error");
}
}
}
4.使用工具类手动校验
手动校验可以参考Hibernate Validator官方文档。这里是一个简单的例子,手动校验,并把结果存在HashMap中。
<参考>
docs.jboss.org/hibernate/v…
public class ValidUtil {
private Validator validator;
private ExecutableValidator executableValidator;
public ValidUtil() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
executableValidator = validator.forExecutables();
}
public boolean validateMethod(Object requestVO, Method method,Object[] parameterValues,HashMap<String, String> result) {
Set<ConstraintViolation<Object>> violations = executableValidator.validateParameters(requestVO, method, parameterValues);
if (violations.isEmpty()) {
return true;
}
setResult(result, violations);
return false;
}
}
5.执行结果
[04 14:42:56,088 ERROR] [http-nio-8080-exec-1] validate.ValidationAspect - key:registHandler.arg0,value:nick must be 4-10 chars or numbers.
[04 14:42:56,088 ERROR] [http-nio-8080-exec-1] validate.ValidationAspect - key:registHandler.arg1,value:age too young