需求描述:
在项目的实际的编写过程当中,通过JSR303针对于@RequestBody 可以进行参数的数据校验,但是如果是通过@RequestParam 或者是@PathVeriable 进行传值却没有什么很好的数据校验方式,通常只能通过:
if("".equals(message) || message == null){
throw new RuntimeException(“Bad Request”);
}
进行校验,所以想要编写一套注解实现对于@RequestParam 和@RequestPattern 的数据校验,实现的需求如下:
- 注解的value值为错误信息
- 注解的参数如果为null则抛出异常,如果为空也抛出异常,将异常交给异常处理器进行处理
- 可以指定异常的类型
结构:
@Param
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @BelongsProject: springbootTest
* @BelongsPackage: com.bazihou.springboottest.annotation
* @Author: Wang Haipeng
* @CreateTime: 2021-11-26 18:08
* @Description: 校验参数
* 配合FieldValid 一起使用,作用于方法的形式参数列表上,可以指定校验错误的数据信息
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Param {
}
@FieldValid
import com.honglv.app.common.exception.SysException;
import java.lang.annotation.*;
/**
* @BelongsProject: springbootTest
* @BelongsPackage: com.bazihou.springboottest.annotation
* @Author: Wang Haipeng
* @CreateTime: 2021-11-28 15:17
* @Description: 校验参数
* 配合FieldParam一起使用,作用在方法上,可以指定验证错误的数据错误类型
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface FieldValid {
}
CheckParamAspect
import com.honglv.app.common.annotation.FieldValid;
import com.honglv.app.common.annotation.Param;
import com.honglv.app.common.enums.ErrorCodeEnum;
import com.honglv.app.common.exception.SysException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* @BelongsProject: springbootTest
* @BelongsPackage: com.bazihou.springboottest.aspect
* @Author: Wang Haipeng
* @CreateTime: 2021-11-26 18:09
* @Description:
*
* 需求:
* 1、被注解的方法可以验证所以@RequestParam 或者 @PathPatern 的参数不为null
* 2、如果为null则抛出异常
* 3、被注解的参数可以验证不为null ,如果为null可以抛出异常
*/
@Aspect
@Component
@Slf4j
public class CheckParamAspect {
@Pointcut("@annotation(com.honglv.app.common.annotation.FieldValid)")
public void pointCut() {
}
/**
* @param joinPoint 切入点参数
* 校验逻辑:
* 获取参数对应的参数名,参数类型,和参数上的注解
* 如果参数上的注解同时有@Param 和@RequestParam 则校验数值
* 如果数据为引用数据类型,且值为null 则校验是否为String 类型,如果为是且为“”则抛出异常,否则校验成功
* 如果值为基本数据类型,因为基本数据类型本身如果没有传递则不会影响数据的传输,所以不进行校验
*/
@Before("pointCut()")
public void around(JoinPoint joinPoint) throws Throwable {
Class<?> pubException = null;
MethodSignature m = (MethodSignature) joinPoint.getSignature();
Method method = m.getMethod();
Class<?>[] parameterTypes = method.getParameterTypes();
Object[] args = joinPoint.getArgs();
Annotation[][] annotations = method.getParameterAnnotations();
for (int i = 0; i < annotations.length; i++) {
int flag = 0;
for (int j = 0; j < annotations[i].length; j++) {
if (annotations[i][j] instanceof RequestParam
|| annotations[i][j] instanceof Param
|| annotations[i][j] instanceof PathVariable) {
flag++;
}
}
/*参数上同时具有@RequestBody 和@Param*/
if (flag >= 2) {
Class type = parameterTypes[i];
/*参数不是基本数据类型*/
if (!type.isPrimitive()) {
if (args[i] == null) {
log.info("@Param数据校验为空");
throw new SysException(ErrorCodeEnum.BADREQUEST);
} else {
if (type.equals(String.class) && "".equals(args[i])) {
log.info("@Param数据校验为空");
throw new SysException(ErrorCodeEnum.BADREQUEST);
}
}
}
}
}
}
}
测试及使用:
/*
如果Method对象被FieldValid注解则返回true
*/
if (method.isAnnotationPresent(FieldValid.class)){
/*获取对应注解对象*/
FieldValid fieldValid = method.getAnnotation(FieldValid.class);
/*根据注解对象调用方法获取值*/
pubException = fieldValid.value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface FieldValid {
/**
* @return 校验错误的数据错误类型
*/
Class value() default SysException.class;
}
Class<?> pubException = null;
if (method.isAnnotationPresent(FieldValid.class)){
FieldValid fieldValid = method.getAnnotation(FieldValid.class);
/*指定的异常类对象的Class对象*/
pubException = fieldValid.value();
}
/*判断自定义异常是不是pubException的父类(pubException是指定的异常类型)*/
if (!SysException.class.isAssignableFrom(pubException)){
exceptionFlag = false;
}
/*如果pubException是自定义异常的父类*/
if (exceptionFlag){
/*反射获取构造方法创建对应的实例对象*/
Constructor<?> constructor = pubException.getConstructor(ErrorCodeEnum.class);
constructor.setAccessible(true);
throw (Throwable) constructor.newInstance(ErrorCodeEnum.BADREQUEST);
}else {
/*不是则抛出自定义异常*/
throw new SysException(ErrorCodeEnum.BADREQUEST);
}
package com.bazihou.springboottest.aspect;
import com.bazihou.springboottest.annotation.FieldValid;
import com.bazihou.springboottest.annotation.Param;
import com.bazihou.springboottest.enums.ErrorCodeEnum;
import com.bazihou.springboottest.exception.SysException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.util.pattern.PathPattern;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @BelongsProject: springbootTest
* @BelongsPackage: com.bazihou.springboottest.aspect
* @Author: Wang Haipeng
* @CreateTime: 2021-11-26 18:09
* @Description:
*
* 需求:
* 1、被注解的方法可以验证所以@RequestParam 或者 @PathPatern 的参数不为null
* 2、如果为null则抛出异常
* 3、被注解的参数可以验证不为null ,如果为null可以抛出异常
*/
@Aspect
@Component
@Slf4j
public class CheckParamAspect {
@Pointcut("@annotation(com.bazihou.springboottest.annotation.FieldValid)")
public void pointCut() {}
@Before("pointCut()")
public void before(JoinPoint joinPoint) throws Throwable {
Class<?> pubException = null;
boolean exceptionFlag = true;
MethodSignature m = (MethodSignature) joinPoint.getSignature();
Method method = m.getMethod();
/*参数类型的Class对象数组*/
Class<?>[] parameterTypes = method.getParameterTypes();
/*参数数据值*/
Object[] args = joinPoint.getArgs();
Annotation[][] annotations = method.getParameterAnnotations();
if (method.isAnnotationPresent(FieldValid.class)){
FieldValid fieldValid = method.getAnnotation(FieldValid.class);
/*指定的异常类对象的Class对象*/
pubException = fieldValid.value();
}
/*判断SysException是不是pubException的父类*/
if (!SysException.class.isAssignableFrom(pubException)){
exceptionFlag = false;
}
for (int i = 0; i < annotations.length; i++) {
int flag = 0;
int index = -1;
for (int j = 0; j < annotations[i].length; j++) {
if (annotations[i][j] instanceof RequestParam
|| annotations[i][j] instanceof PathVariable) {
flag++;
}else if (annotations[i][j] instanceof Param){
flag++;
index = j;
}
}
/*参数上同时具有@RequestBody 和@Param*/
if (flag >= 2) {
/*获取到Param注解的Annotation对象*/
Param param = (Param) annotations[i][index];
Class type = parameterTypes[i];
/*参数不是基本数据类型*/
if (!type.isPrimitive()) {
if (args[i] == null ||
(type.equals(String.class) && "".equals(args[i]))) {
log.info(param.value());
if (exceptionFlag){
Constructor<?> constructor = pubException.getConstructor(ErrorCodeEnum.class);
constructor.setAccessible(true);
throw (Throwable) constructor.newInstance(ErrorCodeEnum.BADREQUEST);
}else {
throw new SysException(ErrorCodeEnum.BADREQUEST);
}
}
}
}
}
}
}