本文通过简单的代码,实现validation自定义数据校验器
项目结构
$ tree -I target -I test
.
├── pom.xml
└── src
└── main
├── java
│ └── com
│ └── example
│ └── demo
│ ├── DemoApplication.java
│ ├── common
│ │ ├── GlobalExceptionHandler.java
│ │ └── JsonResult.java
│ ├── controller
│ │ └── UserController.java
│ ├── dto
│ │ └── UserDTO.java
│ ├── validation
│ │ └── StatusValidation.java
│ └── validator
│ └── StatusValidator.java
└── resources
└── application.properties
validation依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
完整pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spring-boot-validation-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
自定义注解
package com.example.demo.validation;
import com.example.demo.validator.StatusValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({
ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = StatusValidator.class)
public @interface StatusValidation {
String[] statusType() default {
};
String message() default "状态值错误";
Class<?>[] groups() default {
};
Class<? extends Payload>[] payload() default {
};
}
自定义校验器
package com.example.demo.validator;
import com.example.demo.validation.StatusValidation;
import lombok.extern.slf4j.Slf4j;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.List;
@Slf4j
public class StatusValidator implements ConstraintValidator<StatusValidation, Integer> {
private List<String> statusType;
@Override
public void initialize(StatusValidation constraintAnnotation) {
this.statusType = Arrays.asList(constraintAnnotation.statusType());
ConstraintValidator.super.initialize(constraintAnnotation);
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
if (value != null) {
if (!this.statusType.contains(String.valueOf(value))) {
return false;
}
}
return true;
}
}
使用注解
package com.example.demo.dto;
import com.example.demo.validation.StatusValidation;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
@Data
public class UserDTO {
private Long userId;
@NotNull
@Length(min = 2, max = 10)
private String userName;
@NotNull
@Length(min = 6, max = 20)
private String account;
@NotNull
@Length(min = 6, max = 20)
private String password;
@StatusValidation(statusType = {
"1", "2"})
private Integer status;
}
校验数据
package com.example.demo.controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.common.JsonResult;
import com.example.demo.dto.UserDTO;
@RestController
public class UserController {
@PostMapping("/save")
public JsonResult saveUser(
@RequestBody @Validated UserDTO userDTO) {
// 校验通过,才会执行业务逻辑处理
System.out.println(userDTO);
return JsonResult.success(userDTO);
}
}
捕获全局异常
package com.example.demo.common;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolationException;
import java.util.Arrays;
/**
* 全局异常处理
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 参数校验失败
*
* @param ex
* @return
*/
@ExceptionHandler({
MethodArgumentNotValidException.class})
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public JsonResult handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
FieldError fieldError = ex.getBindingResult()
.getFieldErrors()
.get(0);
String msg = fieldError.getField() + fieldError.getDefaultMessage();
return JsonResult.error("参数错误:" + msg);
}
/**
* 参数校验失败
*
* @param ex
* @return
*/
@ExceptionHandler({
ConstraintViolationException.class})
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public JsonResult handleConstraintViolationException(ConstraintViolationException ex) {
return JsonResult.error("参数校验失败" + ex.getMessage());
}
}
统一的数据返回
package com.example.demo.common;
/**
* 统一的数据返回
*/
public class JsonResult {
private Integer code;
private String msg;
private Object data;
public JsonResult(Integer code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static JsonResult success(Object data){
return new JsonResult(0, "success", data);
}
public static JsonResult error(String errorMessage) {
return new JsonResult(-1, errorMessage, null);
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
测试用例及其结果如下
# 参数错误:account不能为null
POST {
{
baseUrl}}/save
content-type: application/json
{
"name": "Tom"
}
###
# 参数错误:password不能为null
POST {
{
baseUrl}}/save
content-type: application/json
{
"userName": "Tom"
}
###
# 参数错误:password长度需要在6和20之间
POST {
{
baseUrl}}/save
content-type: application/json
{
"userName": "Tom",
"password": "11"
}
###
# 参数错误:account不能为null
POST {
{
baseUrl}}/save
content-type: application/json
{
"userName": "Tom",
"password": "1133344"
}
###
# success
POST {
{
baseUrl}}/save
content-type: application/json
{
"userName": "Tom",
"password": "1133344",
"account": "account"
}
###
# 参数错误:status状态值错误
POST {
{
baseUrl}}/save
content-type: application/json
{
"userName": "Tom",
"password": "1133344",
"account": "account",
"status": 0
}
###
###
# success
POST {
{
baseUrl}}/save
content-type: application/json
{
"userName": "Tom",
"password": "1133344",
"account": "account",
"status": 1
}