头图

The role of validation annotations

Before the system executes the business logic, it will check the input data to check whether the data is valid and legal. Therefore, we may write a lot of judgment logic such as if else , especially when the same data appears in different methods, the verification logic code will appear repeatedly, resulting in code redundancy, poor readability and maintainability.

Custom validation annotations

import dependencies

There is a component hibernate-validator in the Hibernate framework specially used for data verification. Although the data layer does not use Hibernate as ORM framework in the usual Spring project, hibernate-validator is often integrated for data verification.

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.1.7.Final</version>
</dependency>

Next, we write an annotation for URL verification to implement a simple URL verification for website information management. We also use the verification tools provided in the ready-made apache toolkit for verification.

<dependency>
    <groupId>commons-validator</groupId>
    <artifactId>commons-validator</artifactId>
    <version>1.7</version>
</dependency>

implementation annotation

Check Comments

/**
 * 会将注解信息包含在javadoc中
 */
@Documented
/**
 * 1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
 * 2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,
 *   这是默认的生命周期;
 * 3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
 *
 * 一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解,比如@Deprecated使用RUNTIME注解
 * 如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;
 * 如果只是做一些检查性的操作,比如 @Override 和 @Deprecated,使用SOURCE 注解。
 */
@Retention(RetentionPolicy.RUNTIME)
/**
 * 作用在字段上
 * TYPE - 作用在类上面
 * FILED - 作用在属性上面
 * METHOD - 作用在方法上
 * CONSTRUCTION - 作用在构造器上
 * PARAM - 作用在方法参数上
 * 允许多种的情况是 @Target({ElementType.FIELD,ElementType.METHOD})
 */
@Target(ElementType.FIELD)
/**
 * 对应校验类
 */
@Constraint(validatedBy = IsUrlValidator.class)
public @interface IsUrl {
    /**
     * 是否 强校验
     * @return
     */
    boolean required() default true;

    /**
     * 校验不通过返回信息
     * @return
     */
    String message() default "请输入正确的url";

    /**
     * 所属分组,即在有分组的情况下,只校验所在分组参数
     * @return
     */
    Class<?>[] groups() default {};

    /**
     * 主要是针对bean,很少使用
     *
     * @return 负载
     */
    Class<? extends Payload>[] payload() default {};

}

Check class

The verification class needs to implement the ConstraintValidator<T,E> interface, the first generic type is the annotation, and the second is the verification data type.

To implement this interface, you must rewrite isValid() method and implement the main verification logic in it.

public class IsUrlValidator implements ConstraintValidator<IsUrl,String> {
    private boolean isRequired;

    /**
     * 初始化,获取是否强校验
     * @param constraintAnnotation
     */
    @Override
    public void initialize(IsUrl constraintAnnotation) {
        isRequired = constraintAnnotation.required();
    }

    @Override
    public boolean isValid(String s, ConstraintValidatorContext context) {
        if (!isRequired){
            return true;
        }else {
            UrlValidator validator = UrlValidator.getInstance();
            return validator.isValid(s);
        }
    }
}

Use custom annotations

Create Insert and Update groups for distinguishing and enabling verification respectively

The class used for grouping needs to inherit the javax.validation.groups.Default interface

public interface Update extends Default {}
public interface Insert extends Default {}

Create a WebSite class, and check the url and alternateUrl . When this field belongs to the Insert group and the Update group, the field check is performed.

public class WebSite {
    /**
     * id
     */
    private Integer id;
    /**
     * 网站名称
     */
    private String name;
    /**
     * 网址
     */
    @IsUrl(groups = Insert.class)
    private String url;
    /**
     * 备用网址
     */
    @IsUrl(groups = Update.class)
    private String alternateUrl;
}

The specific verification method is as follows. The Insert packet is verified on the insert interface, that is, the url attribute is verified, and the Update packet is verified on the updateAlternate interface, that is, the alternateUrl field is verified.

@RestController
@RequestMapping("/website")
public class WebSiteController {
    @RequestMapping("/insert")
    public void insert(@RequestBody @Validated(Insert.class) WebSite site){
        System.out.println(site);
    }

    @RequestMapping("/updateAlternate")
    public void updateAlternateUrl(@RequestBody @Validated(Update.class) WebSite site){
        System.out.println(site);
    }
}

If the verification fails, the code will throw a MethodArgumentNotValidException exception. We implement a unified exception handling class to handle this exception and return the verification prompt information.

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    // 处理接口参数数据格式错误异常
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    public Object errorHandler(HttpServletRequest request, MethodArgumentNotValidException e) {
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();

        String message = allErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(";"));
        log.error("{}请求,发生参数校验异常:{}",request.getServletPath(),message);
        return message;
    }
}

Use the http tool to call the interface and return relevant information

First, the insert interface is called with an incorrect url parameter. The verification fails, but the call to updateAlternate interface can pass.

POST http://localhost:8080/website/insert
Content-Type: application/json
{  
  "id": 1,  
  "name": "百度",  
  "url":"htps://www.baidu.com/",  
  "alternateUrl":"https://www.baidu.com/"
}

###

POST http://localhost:8080/website/updateAlternate
Content-Type: application/json
{  
  "id": 1,  
  "name": "百度",  
  "url":"htps://www.baidu.com/",  
  "alternateUrl":"https://www.baidu.com/"
}

The return and log printing of calling the insert interface are as follows

HTTP/1.1 200 
Content-Type: text/plain;
charset=UTF-8
Content-Length: 21
Date: Wed, 02 Mar 2022 15:30:23 
GMTKeep-Alive: timeout=60
Connection: keep-alive

请输入正确的url
--------------------------------------
xxx.GlobalExceptionHandler : /website/insert请求,发生参数校验异常:请输入正确的url

Common verification notes

annotationParaphrase
@NullThe annotated element must be null
@NotNullThe annotated element must not be null
@AssertTrueThe annotated element must be true
@AssertFalseAnnotated elements must be false
@Min(value)The annotated element must be a number whose value must be greater than or equal to the specified minimum value
@Max(value)The annotated element must be a number whose value must be less than or equal to the specified maximum value
@DecimalMin(value)The annotated element must be a number whose value must be greater than or equal to the specified minimum value
@DecimalMax(value)The annotated element must be a number whose value must be less than or equal to the specified maximum value
@Size(max, min)The size of the annotated element must be within the specified range, and the element must be a collection, representing the number of collections
@Digits (integer, fraction)The annotated element must be a number and its value must be in the acceptable range
@PastThe annotated element must be a date in the past
@FutureThe annotated element must be a future date
@Length(min=, max=)The size of the annotated string must be within the specified range, it must be an array or a string, if it is a microarray, it is expressed as the length of the array, and the string is expressed as the length of the string
@NotEmptyThe annotated string must be non-empty
@Range(min=, max=)The annotated element must be in the appropriate scope
@NotBlankThe annotated string must be non-empty
@Pattern(regexp = )Regular expression check
@ValidObject cascading verification, that is, verifying the properties of objects in an object

eacape
205 声望8 粉丝

JAVA 攻城狮