实现@RequestParam参数值校验

需求描述:

在项目的实际的编写过程当中,通过JSR303针对于@RequestBody 可以进行参数的数据校验,但是如果是通过@RequestParam 或者是@PathVeriable 进行传值却没有什么很好的数据校验方式,通常只能通过:

if("".equals(message) || message == null){

​ throw new RuntimeException(“Bad Request”);

}

进行校验,所以想要编写一套注解实现对于@RequestParam 和@RequestPattern 的数据校验,实现的需求如下:

  • 注解的value值为错误信息
  • 注解的参数如果为null则抛出异常,如果为空也抛出异常,将异常交给异常处理器进行处理
  • 可以指定异常的类型

结构:

image-20211128215423067

@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);
                        }
                    }
                }
            }

        }
    }
}

测试及使用:

2)加强

反射获取注解的参数值

/*
	如果Method对象被FieldValid注解则返回true
*/
if (method.isAnnotationPresent(FieldValid.class)){
    		/*获取对应注解对象*/
            FieldValid fieldValid = method.getAnnotation(FieldValid.class);
            /*根据注解对象调用方法获取值*/
            pubException = fieldValid.value();
        }

创建指定Exception 类型的异常

@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);
                        }

3)最终实现

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);
                        }
                    }
                }
            }

        }
    }
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值