User defined parameter verification annotation

Problem description

Standard interface development is always inseparable from parameter verification. Naturally, Spring has prepared corresponding templates for us. Parameters can be checked and verified by using @ NotEmpty, @ NotBlank, @ NotNull and other annotations, but these annotations must be used with @ Valid to take effect. For details, please refer to: @Valid introduction and related notes - Jianshu (jianshu.com).

There are restrictions on the use of @ Valid, that is, the Validation of spring MVC only takes effect in the Controller. From the perspective of the whole interface ecology, this is a reasonable design, and the data verification does not pass, nor will it reach the business layer.

But what if we need to verify the data in the business layer? And the condition is not only non empty, but also includes some special format or scope requirements. It can be solved naturally by using simple if else, but the code is also complex and redundant.

terms of settlement

The parameter verification provided by spring cannot meet our needs, so write your own annotations to meet your needs.

Requirements: the parameter verification can be realized by adding annotation to the attribute, including non empty and specified regular verification, and the user-defined error information can be returned at the same time

User defined parameter verification annotation

Parameter verification class annotation: @ Validation

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

/**
 * Parameter verification annotation
 * Perform non null and regular verification on the annotated attribute
 *
 * @author wyhao
 * @date 2021/5/18
 **/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE,
        ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validation {

    //error message
    String message() default "Parameter cannot be empty";

    //regular expression 
    String pattern() default "";

}

@Validation validator

package com.example.springtrain.caculate.processor.serviceUtils;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Validation Validator 
 * Verify the parameters with custom annotation @ Validation
 *
 * @author wyhao
 * @date 2021/5/19
 **/
@Slf4j
public class ValidationCheck {

    /**
     * Verification method
     * Scan the attributes in the object to see if there is @ Validation annotation, and verify if there is annotation
     *
     * @param o The object to be verified, such as the input parameter object
     */
    public static void check(Object o) {
        if (null == o) {
            return;
        }
        Class clazz = o.getClass();
        List<Field> fieldList = new ArrayList<Field>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }

        //All properties of the variable object
        fieldList.forEach(field -> {
            field.setAccessible(true);
            try {
                Object value = field.get(o);

                //Get property value
                Validation annotation = field.getAnnotation(Validation.class);
                if (null == annotation) {   //Those not annotated will not be processed
                    return;
                }
                checkNotNull(value, annotation);
                checkPattern(value, annotation);

            } catch (IllegalArgumentException | IllegalAccessException e) {
                log.info("Validation Validator data parsing failed:{}", e.getMessage());
            }
        });
    }

    /**
     * Non null judgment
     *
     * @param value      Attribute value
     * @param validation Annotation information
     */
    private static void checkNotNull(Object value, Validation validation) {
        //log.info("start check is not empty");
        if (validation != null && value == null) {
            throw new RuntimeException(validation.message());
        }
    }

    /**
     * Regular check
     *
     * @param value      Attribute value
     * @param validation Annotation information
     */
    private static void checkPattern(Object value, Validation validation) {
        if (null != validation.pattern() && validation.pattern().length() > 0) {//Existence regularity
            //Compile and assign the regular expression given in validation to the Pattern class
            Pattern p = Pattern.compile(validation.pattern());
            //Match the value in value with the rule of p
            Matcher m = p.matcher(value.toString());
            if (!m.matches()) { //If the attribute value does not conform to the format specified by the regular, an exception is thrown
                throw new RuntimeException(validation.message());
            }
        }
    }
}

Annotation use

Add verification rules to the class attributes to be verified:

@Data
public class Father {
    @Validation(message = "Name cannot be blank")		//Non null check
    private String name;

    @Validation(pattern = "\\d+",message = "Age can only be a number")	//Regular check, of course, the premise is also non empty
    private String age;

    public Father(String name, String age) {
        this.name = name;
        this.age = age;
    }

    public Father() {
    }
}

First of all, our annotations must not affect the normal use. Here are the data that meet the requirements, and the code runs normally

Father father1 = new Father("Laodie","56");
ValidationCheck.check(father1);	//Perform parameter verification
System.out.println(father1);

When name is empty, the following code will directly throw an exception and prompt that the name cannot be empty

Father father2 = new Father(null,"56");
ValidationCheck.check(father2);	//Perform parameter verification
System.out.println(father2);

When the age does not meet the requirement that it must be a number, it will throw an exception and prompt that the age can only be a number

Father father3 = new Father(null,"a56");
ValidationCheck.check(father3);
System.out.println(father3);

If we have other business needs, we can add corresponding attributes in the annotation and add custom rules in the verifier, which has certain expansibility.

Continuous optimization

You also need to use validationcheck every time you use it Code such as check (father1) may be troublesome, because the author is lazy because he doesn't need many parameter verification places. In fact, we can write another annotation and use it in the code or directly on the class, like this:

//Add to the code
@ValidationCheck	//Perform parameter verification
Father father2 = new Father(null,"56");


//Or add class
@ValidationCheck
public class ValidationTest {
    @Test
    public void test() {

        Father father1 = new Father("Laodie","56");
        System.out.println(father1);

        Father father2 = new Father(null,"56");
        System.out.println(father2);

        Father father3 = new Father(null,"a56");
        System.out.println(father3);
    }
}

Keywords: Java interface

Added by jubripley on Sun, 30 Jan 2022 20:38:49 +0200