In an article on https://juejin.cn/post/6844903551362138119, There is no spring MVC validation mechanism for @PathVariable and @RequestParam to validate @valid + BindingResult. At this time, the verification can only be carried out on the code one by one.

Let’s start with @pathVariable and @requestParam; The use of two notes.

1. Original version explanation

1.1 @ PathVariable

1.1.1 RESTful style

Format: PATH /1/catalpaFlat eg:

@GetMapping("path/{isInt}/{isString}")
public ResponseVO pathGet(@PathVariable Integer isInt, @PathVariable String isString) {
   log.info("int:" + isInt);
   log.info("String:" + isString);
  JSONObject resultJson = new JSONObject();
  resultJson.put("isInt", isInt);
  resultJson.put("isString", isString);
  return new ResponseVO(HttpStatus.OK.value(), "pathGet", resultJson);
}
Copy the code

Request: http://localhost:8888/path/3/dadas

1.1.2 check

Code check

if(isInt < 2) {return new ResponseVO(HttpStatus.BAD_REQUEST.value(), "pathGet"."isInt must be more than 2");
}
Copy the code

Code verification is personally unnecessary, unless it is for special needs

1.2 @ RequestParam

1.2.1 Form Submission (Query)

Format: query? isInt=2&isString=catalpaFlat

@GetMapping("query?")
public ResponseVO queryGet(@RequestParam Integer isInt, @RequestParam String isString) {
    log.info("int:" + isInt);
    log.info("String:" + isString);
    JSONObject resultJson = new JSONObject();
    resultJson.put("isInt", isInt);
    resultJson.put("isString", isString);
    return new ResponseVO(HttpStatus.OK.value(), "queryGet", resultJson);
}
Copy the code

1.2.2 check

Code verification is also required

if(isInt < 2) {return new ResponseVO(HttpStatus.BAD_REQUEST.value(), "queryGet"."isInt must be more than 2");
}
Copy the code

Because @valid + BindingResult can only be used for @responseBody annotations.

2. AOP improved version

If you use @pathVariable or @requestParam, you need to do a wave of validation. If you use @PathVariable or @requestParam, you need to do a wave of validation.

2.1 Interface Layer (IDAL)

The following code is also modified:

  • The value of @parameterValid is equivalent to @valid, and parameter verification rules are configured in its property
  • The @pathAndQueryParamValid equivalent of an AOP pointcut
@PathAndQueryParamValid
@GetMapping("path/{isInt}/{isString}")
public ResponseVO pathGet(@PathVariable @ParameterValid(type = Integer.class, msg = "isInt must be more than 2", isMin = true, min = 2) Integer isInt,
                          @PathVariable @ParameterValid(type = String.class, msg = "isString is empty") String isString) {
    log.info("int:" + isInt);
    log.info("String:" + isString);
    JSONObject resultJson = new JSONObject();
    resultJson.put("isInt", isInt);
    resultJson.put("isString", isString);
    return new ResponseVO(HttpStatus.OK.value(), "pathGet", resultJson);
}
@GetMapping("query")
@PathAndQueryParamValid
public ResponseVO queryGet(@RequestParam @ParameterValid(type = Integer.class, msg = "isInt must be more than 2 ", isMin = true, min = 2) Integer isInt,
                          @RequestParam @ParameterValid(type = String.class, msg = "isString is empty") String isString) {
   log.info("int:" + isInt);
   log.info("String:" + isString);
   JSONObject resultJson = new JSONObject();
   resultJson.put("isInt", isInt);
   resultJson.put("isString", isString);
   return new ResponseVO(HttpStatus.OK.value(), "queryGet", resultJson);
}
Copy the code

2.2 Custom Annotations

2.2.1 @ PathAndQueryParamValid

Just a simple annotation for method types:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public  @interface  PathAndQueryParamValid {
}
Copy the code

2.2.1 @ ParameterValid

@parameterValid You can add verification rules based on actual service requirements:

@Documented
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interfaceParameterValid { Class<? > type();String msg(a);

    boolean request(a) default true;

    boolean isEmpty(a) default true;

    boolean isBlank(a) default true;

    boolean isNull(a) default false;

    int min(a) default 0;
    int max(a) default 0;
    int[] section() default {0.0};
    boolean isMin(a) default false;
    boolean isMax(a) default false;
    boolean isSection(a) default false;
}
Copy the code

2.3 AOP Aspect (Emphasis -1)

  • Use joinPoint to get the method name and class name of the pointcut
  • Obtain method parameters using JoinPoint
  • Call (Emphasis 2) ParamValidSupport
  • AdvanceResponseSupport is nothing more than a response, as in the previous article.
@Aspect
@Component
public class PathAndQueryParamValidAspect {

  private static final Logger log = LoggerFactory.getLogger(PathAndQueryParamValidAspect.class);

  @Before("@annotation(paramValid)")
  public void paramValid(JoinPoint joinPoint, PathAndQueryParamValid paramValid) {
      String className = joinPoint.getTarget().getClass().getName();
      String methodName = joinPoint.getSignature().getName();
      Object[] param = joinPoint.getArgs();
      try {
          List<String> errorLists = ParamValidSupport.get().validate(className, methodName,
                  ParameterValid.class, param);
          if(errorLists ! =null) {
              AdvanceResponseSupport.advanceResponse(
                      new ResponseVO(HttpStatus.BAD_REQUEST.value(), "parameter empty", errorLists)); }}catch (NotFoundException | NoSuchMethodException | ClassNotFoundException e) {
          log.error("E - name:" + e.getClass().getName() + ": message:"+ e.getMessage()); }}}Copy the code

2.4 Verification (Key -2)

  • Using the method name and class name, you can obtain CtClass and CtMethod by ClassPool
  • To obtain parameter annotation, parsing parameter annotation rules to verify parameters

The only refactoring that can be done here is to validate the int and string types, and you can add other requirements. Present the main content first)

public class ParamValidSupport {
private static final Logger logger = LoggerFactory.getLogger(ParamValidSupport.class);

private static final String PARAM_TYPE_ERROR = "param type error";
private static final String INT_PARAM_ERROR = "Invalid interva";
private static final int INT_PARAM_TYPE_MAX_SIZE = 2;
private static final int INT_PARAM_SIZE_SUBSCRIPT_MIN = 0;
private static final int INT_PARAM_SIZE_SUBSCRIPT_MAX = 0;

private static final int STRING_SIZE = 2;
private static final char STRING_TYPE_END = '} ';
private static final char STRING_TYPE_BEGIN = '{';
private static final char STRING_EMPTY_DOUBLE_CHARACTER = '"';
private static final char STRING_EMPTY_SINGLE_CHARACTER = '\' ';

private static ParamValidSupport mInstance;

private ParamValidSupport(a) {}public static ParamValidSupport get(a) {
    if (mInstance == null) {
        synchronized (ParamValidSupport.class) {
            if (mInstance == null) {
                mInstance = newParamValidSupport(); }}}return mInstance;
}

 /** ** check */
public List<String> validate(String className, String methodName, Class
        annotationClass, Object[] args)
        throws NotFoundException, NoSuchMethodException, ClassNotFoundException {

    if (StringUtils.isBlank(className)) {
        return null;
    }
    if (StringUtils.isBlank(methodName)) {
        return null;
    }
    if (annotationClass == null) {
        return null;
    }

    ClassPool pool = ClassPool.getDefault();
    CtClass ct = pool.get(className);
    CtMethod ctMethod = ct.getDeclaredMethod(methodName);
    Object[][] parameterAnnotations = ctMethod.getParameterAnnotations();

    List<String> errorLists = new ArrayList<>();

    for (int i = 0; i < parameterAnnotations.length; i++) {
        Object[] parameterAnnotation = parameterAnnotations[i];
        Object param = args[i];
        for (Object object : parameterAnnotation) {
            Annotation annotation = (Annotation) object;
            Class<? extends Annotation> aClass = annotation.annotationType();
            if (aClass.equals(annotationClass)) {
                boolean isEmpty = ((ParameterValid) object).isEmpty();
                if (isEmpty) {
                    ParameterValid parameterValid = (ParameterValid) object;
                    String errorMsg = parameterValid.msg();
                    if (Integer.class.isAssignableFrom(param.getClass())){
                        int paramInt = (int) param;
                        if (parameterValid.isMin() && paramInt < parameterValid.min()) {
                            errorLists.add(errorMsg);
                        }
                        if (parameterValid.isMax() && paramInt < parameterValid.max()) {
                            errorLists.add(errorMsg);
                        }
                        if (parameterValid.isSection()) {
                            int[] section = parameterValid.section();
                            if(section.length ! = INT_PARAM_TYPE_MAX_SIZE) { logger.error(INT_PARAM_ERROR);throw new ParameterValidException(INT_PARAM_ERROR);
                            }
                            if(! (paramInt > section[INT_PARAM_SIZE_SUBSCRIPT_MIN] && paramInt < section[INT_PARAM_SIZE_SUBSCRIPT_MAX])) { errorLists.add(errorMsg); }else if(! (paramInt > section[INT_PARAM_SIZE_SUBSCRIPT_MAX] && paramInt < section[INT_PARAM_SIZE_SUBSCRIPT_MIN])) { errorLists.add(errorMsg); }}}if (String.class.isAssignableFrom(param.getClass())){
                        String paramStr = (String) param;
                        if (parameterValid.isNull()) {
                            if(StringUtils.isEmpty(paramStr)) { errorLists.add(errorMsg); }}else {
                            if (parameterValid.isBlank()) {
                                if (StringUtils.isBlank(paramStr)) {
                                    errorLists.add(errorMsg);
                                } else {
                                    int length = paramStr.length();
                                    char begin = paramStr.charAt(0);
                                    char end = paramStr.charAt(length - 1);
                                    if (STRING_TYPE_BEGIN == begin &&
                                            STRING_TYPE_END == end) {
                                        errorLists.add(errorMsg);
                                    }
                                    if (length == STRING_SIZE && STRING_EMPTY_DOUBLE_CHARACTER == begin
                                            && STRING_EMPTY_DOUBLE_CHARACTER == end) {
                                        errorLists.add(errorMsg);
                                    }
                                    if (length == STRING_SIZE && STRING_EMPTY_SINGLE_CHARACTER == begin
                                            && STRING_EMPTY_SINGLE_CHARACTER == end) {
                                        errorLists.add(errorMsg);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    if(errorLists.size() ! =0) {
        return errorLists;
    }
    return null; }}Copy the code

2.4 Test Results