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
annotation | Paraphrase |
---|---|
@Null | The annotated element must be null |
@NotNull | The annotated element must not be null |
@AssertTrue | The annotated element must be true |
@AssertFalse | Annotated 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 |
@Past | The annotated element must be a date in the past |
@Future | The 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 |
@NotEmpty | The annotated string must be non-empty |
@Range(min=, max=) | The annotated element must be in the appropriate scope |
@NotBlank | The annotated string must be non-empty |
@Pattern(regexp = ) | Regular expression check |
@Valid | Object cascading verification, that is, verifying the properties of objects in an object |
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。