服务端参数校验(二):SpringMVC项目的参数校验

项目参见:

https://gitee.com/xxssyyyyssxx/validation

compile "top.jfunc.validation:validation-core:1.0.1"
compile "top.jfunc.validation:validation-spring:1.0.1"

上文提供了参数校验工具,但是如果我们把参数校验跟正常业务逻辑放一起,势必会严重影响代码的可读性。站在Spring巨人的肩头上,利用AOP+注解实现参数校验和正常业务逻辑的解耦。

首先定义校验器Validator,使用者需要实现此接口实现自己的校验逻辑,并把其放入Spring容器,也由于此可以注入Spring容器中的组件供其使用。校验器Validator能够拿到被校验方法的输入参数。

public interface Validator {
    /**
     * 对传入的参数进行校验
     * @param params 传入的参数
     * @throws IllegalArgumentException 参数校验不过抛出异常
     */
    void validate(Object[] params) throws IllegalArgumentException;
}

比如:

/**
 * 注册校验
 * @author xiongshiyan at 2019/9/28 , contact me with email [email protected] or phone 15208384257
 */
@Component
public class StoreRegisterValidator implements Validator {
    @Autowired
    private MemberService memberService;
    @Override
    public void validate(Object[] params) throws IllegalArgumentException {
        JSONObject jsonObject = JSON.parseObject(params[0].toString());

        ValidateValue.with(jsonObject.getString("name")).notNull("名字不允许为空")
                .and(jsonObject.getString("idCard")).notNull("身份证不允许为空")
                .and(jsonObject.getString("cardFront")).notNull("身份证正面照片不允许为空")
                .and(jsonObject.getString("cardBack")).notNull("身份证反面照片不允许为空");

        Member byPhone = memberService.findByPhone(jsonObject.getString("phone"));
        if(null != byPhone){
            throw new IllegalArgumentException("此号码已经存在,不允许重复注册");
        }
    }
}

再定义注解Validated,方法标注了此注解表示要进行参数校验。此注解指定使用哪些Validator进行校验。

/**
 * 添加了此注解就表示要进行参数校验
 * @author xiongshiyan at 2018/11/2 , contact me with email [email protected] or phone 15208384257
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {
    Class<? extends Validator>[] value();
}

在需要参数校验的方法上标注该注解:

@RequestMapping(value = "/register",method ={ RequestMethod.POST})
@Validated(StoreRegisterValidator.class)
public ResponseMsg register(@RequestBody String body){
    。。。。
}

再定义切面,在切入点之前调用注解指定的校验器的校验方法,传入参数,当校验失败的时候就抛出IllegalArgumentException异常:

/**
 * @author xiongshiyan
 * 示例:参数校验切面
 * 使用者可以通过定义自己的切面,在调用方法之前调用{@see ValidateUtil#validateJoinPointParams}
 */
@Aspect
@Component
public class ParamValidateAspect implements Ordered , ApplicationContextAware{
    private ApplicationContext applicationContext;


    @Pointcut("execution(public * top.jfunc.validation.controller..*.*(..))")
    public void webParamValid(){}

    @Around(value = "webParamValid()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        ValidateUtil.validateJoinPointParams(applicationContext , pjp);
        return pjp.proceed();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
public class ValidateUtil {
    /**
     * 在切面方法前调用此方法,根据注解{@link Validated}指定的{@link Validator}校验方法的参数
     * @param applicationContext ApplicationContext
     * @param pjp JoinPoint 在此切入点之前调用
     */
    public static void validateJoinPointParams(ApplicationContext applicationContext , JoinPoint pjp) {
        //目标方法
        Method method = AnnotationUtil.getMethod(pjp);
        Validated validated = AnnotationUtil.getValidated(method);
        //存在@Validated注解
        if(null != validated){
            Class<? extends Validator>[] validatorClasses = validated.value();
            for (Class<? extends Validator> validatorClass : validatorClasses) {
                Validator validator = applicationContext.getBean(validatorClass);
                validator.validate(pjp.getArgs());
            }
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章