Appearance
Knife4j和Swagger3注解使用与SpringBoot各种参数校验
1、Knife4j和Swagger3注解使用
1、Knife4j是什么
Knife4j
是一个集Swagger3
和 OpenAPI3
为一体的增强解决方案
2、pom.xml
spring-boot-param-validation
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xx</groupId>
<artifactId>spring-boot-param-validation</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.8</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.19</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
3、application.yml
yaml
server:
port: 8080
servlet:
context-path: /param
4、启动类
java
package com.xx;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.util.StopWatch;
import java.net.InetAddress;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 09:11
*/
@SpringBootApplication
@Slf4j
public class PramValidationApplication {
@SneakyThrows
public static void main(String[] args){
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext application = SpringApplication.run(PramValidationApplication.class, args);
stopWatch.stop();
Environment env = application.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
String path = env.getProperty("server.servlet.context-path");
log.info("\n--------------------------------------------------------\n\t" +
"Application Manager is running! Access URLs:\n\t" +
"Local: \t\thttp://127.0.0.1:" + port + path + "/\n\t" +
"External: \thttp://" + ip + ":" + port + path + "/\n\t" +
"Swagger文档: \thttp://" + ip + ":" + port + path + "/doc.html\n\t" +
"服务启动完成,耗时: \t" + stopWatch.getTotalTimeSeconds() + "S\n" +
"----------------------------------------------------------");
}
}
使用
@SneakyThrows
注解时,需要注意以下几点:
@SneakyThrows
注解只能用于方法上,不能用于字段、构造函数等其他地方。 方法上使用了@SneakyThrows
注解后,编译器会忽略该方法中的受检查异常,并自动生成异常抛出的代码。使用
@SneakyThrow
注解时要谨慎,确保在方法中的异常处理逻辑充分而且合理。因为异常被转换为运行时异常,可能会隐藏原始的异常信息,增加调试的难度。
@SneakyThrows
注解可以配合使用多个异常类型,比如 @SneakyThrows({IOException.class, InterruptedException.class})。需要注意的是,Lombok 是一个Java库,用于通过注解自动消除样板代码。它可以减少代码量,提高开发效率,但在使用之前,请确保已经熟悉并理解所使用的注解的作用和影响。
5、修改主界面信息
java
package com.xx.config;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 09:33
*/
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("小薛博客官方文档")
.version("1.0")
.description( "`我是小薛博客官方文档`,**你知道吗**")
.termsOfService("https://blog.xueqimiao.com/")
.license(new License().name("Apache 2.0")
.url("https://blog.xueqimiao.com/")));
}
}
修改扫包:
yaml
springdoc:
group-configs:
- group: 'xx'
paths-to-match: '/**'
# 生成文档所需的扫包路径,一般为启动类目录 可以不配置 会自动识别
packages-to-scan: com.xx.controller
6、@Tag与@Operation
java
package com.xx.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 09:13
*/
@Slf4j
@RestController
@RequestMapping("/user")
@Tag(name = "用户管理")
public class UserController {
@Operation(summary = "创建用户",description = "根据姓名创建用户")
@GetMapping("/create")
public ResponseEntity<String> create(String name){
return ResponseEntity.ok(name);
}
}
7、@Schema
java
package com.xx.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 09:13
*/
@Data
public class UserDTO implements Serializable {
@Schema(title = "userId", description = "主键id", defaultValue = "1")
private String id;
@Schema(description = "名称", defaultValue = "张飞")
private String name;
@Schema(description = "年龄", defaultValue = "18", hidden = true)
private String age;
}
java
@Schema(description = "状态", allowableValues = {"Y", "N"})
private String validStatus;

java
@Operation(summary = "创建用户-createOne",description = "根据姓名创建用户1")
@PostMapping("/createOne")
public ResponseEntity<UserDTO> createOne(@RequestBody UserDTO user){
return ResponseEntity.ok(user);
}
8、@Parameter
java
@Operation(summary = "获取用户信息", description = "根据id获取用户信息")
@PostMapping("/getUserById")
@Parameter(name = "id", description = "用户id", in = ParameterIn.QUERY, required = true, example = "6")
public ResponseEntity<String> getUserById(String id) {
return ResponseEntity.ok(id);
}
java
@Operation(summary = "获取用户信息", description = "根据姓名、年龄获取用户信息")
@PostMapping("/getUserByNameAndAge")
@Parameters({
@Parameter(name = "id", description = "用户id", in = ParameterIn.QUERY, required = true, example = "6"),
@Parameter(name = "name", description = "用户姓名", in = ParameterIn.QUERY, required = true, example = "00")
})
public ResponseEntity<String> getUserByNameAndAge(String id, String name) {
return ResponseEntity.ok(id);
}
9、接口添加作者
需要通过配置yml配置文件开启增强功能
yaml
knife4j:
enable: true
接口上:
java
@ApiOperationSupport(author = "xx")
@Operation(summary = "创建用户", description = "根据姓名创建用户")
@GetMapping("/create")
public ResponseEntity<String> create(String name) {
return ResponseEntity.ok(name);
}
Controller上:
java
@ApiSupport(author = "xxxx")
所代表的意思是该Controller模块下所有的接口都是该作者负责开发,当然用@ApiOperationSupport的注解也能覆盖
10、生产环境关闭文档
yaml
knife4j:
enable: true
# 开启生产环境屏蔽
production: true
11、Basic认证功能
yaml
knife4j:
enable: true
# 开启生产环境屏蔽
# production: true
# 开启Swagger的Basic认证功能,默认是false
basic:
enable: true
# Basic认证用户名
username: test
# Basic认证密码
password: 123
12、接口排序
排序规则是使用Knife4j
提供的增强注解@ApiOperationSupport
中的order字段
java
package com.xx.controller;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.xx.dto.UserDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 09:13
*/
@Slf4j
@RestController
@RequestMapping("/user")
@Tag(name = "用户管理")
@ApiSupport(author = "xxxx")
public class UserController {
@ApiOperationSupport(author = "xx",order = 1)
@Operation(summary = "创建用户", description = "根据姓名创建用户")
@GetMapping("/create")
public ResponseEntity<String> create(String name) {
return ResponseEntity.ok(name);
}
@ApiOperationSupport(author = "xx",order = 2)
@Operation(summary = "创建用户-createOne", description = "根据姓名创建用户1")
@PostMapping("/createOne")
public ResponseEntity<UserDTO> createOne(@RequestBody UserDTO user) {
return ResponseEntity.ok(user);
}
@ApiOperationSupport(author = "xx",order = 3)
@Operation(summary = "获取用户信息", description = "根据id获取用户信息")
@PostMapping("/getUserById")
@Parameter(name = "id", description = "用户id", in = ParameterIn.QUERY, required = true, example = "6")
public ResponseEntity<String> getUserById(String id) {
return ResponseEntity.ok(id);
}
@ApiOperationSupport(author = "xx",order = 4)
@Operation(summary = "获取用户信息", description = "根据姓名、年龄获取用户信息")
@PostMapping("/getUserByNameAndAge")
@Parameters({
@Parameter(name = "id", description = "用户id", in = ParameterIn.QUERY, required = true, example = "6"),
@Parameter(name = "name", description = "用户姓名", in = ParameterIn.QUERY, required = true, example = "00")
})
public ResponseEntity<String> getUserByNameAndAge(String id, String name) {
return ResponseEntity.ok(id);
}
}
2、SpringBoot参数校验
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.xx</groupId>
<artifactId>xx-common-core</artifactId>
<version>1.4.0</version>
<dependency>
1、非空校验
JAVA
@NotNull(message = "用户id不能为空")
private String id;
@NotBlank(message = "用户名不能为空")
private String name;
@NotEmpty(message = "houseIds不能为空")
private List<Integer> houseIds;
java
@Operation(summary = "创建用户-createOne", description = "根据姓名创建用户1")
@PostMapping("/createOne")
public ResponseEntity<UserDTO> createOne(@RequestBody @Validated UserDTO user) {
return ResponseEntity.ok(user);
}
@NotNull
: 它用于标记一个属性或方法参数不能为空。它适用于任何类型的参数,包括字符串、集合、数组等。如果一个参数被标记为 @NotNull
,在校验过程中,如果该参数的值为 null
,将会触发校验失败,并返回相应的错误信息。
@NotBlank
: 它用于标记一个字符串类型的属性或方法参数不能为空,并且不能只包含空格。它会先对参数进行 @NotNull
的非空校验,然后再对字符串进行额外的校验。如果参数的值为 null
或者只包含空格
,将会触发校验失败,并返回相应的错误信息。
@NotEmpty
:用于限制集合、数组、Map 等类型属性值不能为 null 或空。
JSR-303 是 Java Specification Request 303 的缩写,它定义了 Java 中用于对象校验的标准规范,即 Bean Validation 规范。
JSR-380 是 Java Specification Request 380 的缩写,它是 JSR-303 规范的升级版,也被称为 Bean Validation 2.0 规范。
2、自定义异常
https://xiaoxueblog.com/springboot/08、全局异常处理.html
java
package com.xx.handler;
import com.xx.common.Result;
import com.xx.common.ResultCodeEnum;
import com.xx.utils.FunctionUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
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.RestControllerAdvice;
import java.util.List;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 11:31
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 集中处理参数丢失、缺少参数、参数为空 情况异常
*
* @param ex
* @return
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseBody
public Result<?> methodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException ex) {
BindingResult bindingResult = ex.getBindingResult();
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
List<String> messageMap = FunctionUtils.map(fieldErrors, FieldError::getDefaultMessage);
// 逗号拼接
String message = String.join(",", messageMap);
return Result.error(ResultCodeEnum.PARAM_VERIFICATION_FAIL.getCode(), message);
}
@ExceptionHandler(Exception.class)
public Result<?> handleException(Exception e) {
log.error(e.getMessage(), e);
return Result.error("操作失败," + e.getMessage());
}
}
3、长度校验
java
@NotBlank(message = "用户名不能为空")
@Length(min = 2, max = 10, message = "用户名长度必须在2-10之间")
private String name;
4、范围校验
java
// 数字
@Min(value = 1, message = "年龄不能小于1")
@Max(value = 100, message = "年龄不能大于100")
private Integer age;
@Range(min = 1, max = 100, message = "年龄2必须在1到100之间")
private Integer age2;
java
// 金额
@NotNull(message = "金额不能为空")
@DecimalMin(value = "0.01", message = "金额不能小于0.01")
@DecimalMax(value = "10.00", message = "金额不能大于10.00")
private BigDecimal amount;
java
// 集合
@Size(min = 1, max = 3, message = "houseIds长度必须在1-3之间")
private List<Integer> houseIds;
java
// 日期
@Future(message = "日期必须是未来的日期")
private Date futureDate;
@Past(message = "日期必须是过去的日期")
private Date pastDate;
5、拓展SpringBoot日期格式化
java
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
private Date futureDate;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
private Date pastDate;
6、正则校验
java
@Pattern(regexp = "^1\\d{10}$", message = "手机号格式不正确")
private String phone;
7、邮箱校验
java
@Email(message = "邮箱格式不正确")
private String email;
8、RequestParam/PathVariable参数校验
添加全局异常
java
@ExceptionHandler(value = ConstraintViolationException.class)
@ResponseBody
public Result<?> constraintViolationException(HttpServletRequest request, ConstraintViolationException ex) {
Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
List<String> messageList = new ArrayList<>();
constraintViolations.forEach(constraintViolation -> {
log.error("参数验证失败:{}", constraintViolation.getMessage());
messageList.add(constraintViolation.getMessage());
});
return Result.error(ResultCodeEnum.PARAM_VERIFICATION_FAIL.getCode(), String.join(",", messageList));
}
java
@Slf4j
@RestController
@RequestMapping("/user")
@Tag(name = "用户管理")
@ApiSupport(author = "xxxx")
@Validated // 记得加
public class UserController {
@ApiOperationSupport(author = "xx",order = 3)
@Operation(summary = "获取用户信息", description = "根据id获取用户信息")
@GetMapping("{userId}")
public ResponseEntity<Integer> getById(@PathVariable("userId") @Min(value = 1,message = "用户id不能小于1") Integer userId) {
return ResponseEntity.ok(userId);
}
@ApiOperationSupport(author = "xx",order = 3)
@Operation(summary = "获取用户信息2", description = "根据id获取用户信息")
@GetMapping("/getById2")
public ResponseEntity<Integer> getById2(@RequestParam("userId") @Min(value = 1,message = "用户id不能小于1") Integer userId) {
return ResponseEntity.ok(userId);
}
}
9、分组校验
通常新增用户
与修改用户
会使用同一个DTO
对象,但是id
只有在修改的时候必传
,新增的时候不需要
。
新增 增删改查
分组
java
package com.xx.validation.group;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 13:23
*/
public interface SaveGroup {
}
java
package com.xx.validation.group;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 13:24
*/
public interface DeleteGroup {
}
java
package com.xx.validation.group;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 13:24
*/
public interface UpdateGroup {
}
java
package com.xx.validation.group;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 13:24
*/
public interface SelectGroup {
}
@Validated(SaveGroup.class)
@Validated(UpdateGroup.class)
java
@ApiOperationSupport(author = "xx",order = 2)
@Operation(summary = "创建用户-createOne", description = "根据姓名创建用户1")
@PostMapping("/createOne")
public ResponseEntity<UserDTO> createOne(@RequestBody @Validated(SaveGroup.class) UserDTO user) {
return ResponseEntity.ok(user);
}
@ApiOperationSupport(author = "xx",order = 2)
@Operation(summary = "修改用户", description = "根据姓名创建用户")
@PostMapping("/updateOne")
public ResponseEntity<UserDTO> updateOne(@RequestBody @Validated(UpdateGroup.class) UserDTO user) {
return ResponseEntity.ok(user);
}
java
@NotNull(message = "用户id不能为空",groups = {UpdateGroup.class})
private String id;
10、嵌套校验
java
package com.xx.dto;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.math.BigDecimal;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 13:36
*/
@Data
public class WorkDTO {
@NotBlank(message = "公司名称不能为空")
private String company;
@DecimalMin(value = "0.01", message = "工资不能小于0.01")
@DecimalMax(value = "10.00", message = "工资不能大于10.00")
private BigDecimal salary;
}
java
// UserDTO 添加属性
@Schema(description = "工作信息")
@NotNull(message = "工作信息不能为空")
@Valid
private WorkDTO workDTO;
java
@ApiOperationSupport(author = "xx",order = 2)
@Operation(summary = "创建用户-createOne2", description = "根据姓名创建用户2")
@PostMapping("/createOne2")
public ResponseEntity<UserDTO> createOne2(@RequestBody @Validated UserDTO user) {
return ResponseEntity.ok(user);
}
需要注意的是,此时DTO类的对应字段必须标记
@Valid
注解
11、自定义校验实现只能输入特定的枚举值
java
package com.xx.validation;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 13:47
*/
public class ConstantEnumValueValidator implements ConstraintValidator<ConstantEnumValue, Object> {
private String[] strValues;
private int[] intValues;
@Override
public void initialize(ConstantEnumValue constraintAnnotation) {
strValues = constraintAnnotation.strValues();
intValues = constraintAnnotation.intValues();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value instanceof String) {
for (String s : strValues) {
if (s.equals(value)) {
return true;
}
}
} else if (value instanceof Integer) {
for (int s : intValues) {
if (s == ((Integer) value).intValue()) {
return true;
}
}
}
return false;
}
}
java
package com.xx.validation;
import com.baomidou.mybatisplus.annotation.EnumValue;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 13:47
*/
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {ConstantEnumValueValidator.class})
public @interface ConstantEnumValue {
// 默认错误消息
String message() default "必须为指定值";
String[] strValues() default {};
int[] intValues() default {};
// 分组
Class<?>[] groups() default {};
// 负载
Class<? extends Payload>[] payload() default {};
// 指定多个时使用
@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented
@interface List {
EnumValue[] value();
}
}
java
@ConstantEnumValue(strValues = {"Y", "N"}, message = "属性状态只能为Y或者N")
private String validStatus;
12、自定义校验实现日期范围
java
package com.xx.validation;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 13:54
*/
@Documented
@Constraint(validatedBy = DateRangeValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface DateRange {
String message() default "日期超出范围";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String min(); // 最小日期
String max(); // 最大日期
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface List {
DateRange[] value();
}
}
java
package com.xx.validation;
import com.xx.utils.DateTimeUtil;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.Date;
/**
* @Author: xueqimiao
* @Date: 2024/3/4 13:51
*/
public class DateRangeValidator implements ConstraintValidator<DateRange, Date> {
private Date minDate;
private Date maxDate;
@Override
public void initialize(DateRange constraintAnnotation) {
this.minDate = DateTimeUtil.parseToDate(constraintAnnotation.min());
this.maxDate = DateTimeUtil.parseToDate(constraintAnnotation.max());
}
@Override
public boolean isValid(Date value, ConstraintValidatorContext context) {
if (value == null) {
return true; // 允许为空
}
return value.compareTo(minDate) >= 0 && value.compareTo(maxDate) <= 0;
}
}
java
@DateRange(min = "2000-01-01", max = "2023-01-01", message = "生日超出范围")
private Date birthday;
13、自定义校验枚举
之前的常量校验弊端 比如加了一个状态 字段上也得跟着改
java
package com.xx.validation;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
/**
* @Author: xueqimiao
* @Date: 2024/8/8 09:14
*/
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {EnumFieldValidator.class})
public @interface EnumFieldValid {
// 默认错误消息
String message() default "必须为指定的枚举值";
// 枚举类
Class<? extends Enum<?>> enumClass();
// 枚举字段方法
String enumFieldMethod();
// 是否允许为空
boolean allowNull() default false;
// 分组
Class<?>[] groups() default {};
// 负载
Class<? extends Payload>[] payload() default {};
}
java
package com.xx.validation;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.lang.reflect.Method;
/**
* @Author: xueqimiao
* @Date: 2024/8/8 09:14
*/
public class EnumFieldValidator implements ConstraintValidator<EnumFieldValid, Object> {
private Class<? extends Enum<?>> enumClass;
private Method enumFieldMethod;
private boolean allowNull;
@Override
public void initialize(EnumFieldValid constraintAnnotation) {
enumClass = constraintAnnotation.enumClass();
allowNull = constraintAnnotation.allowNull();
try {
enumFieldMethod = enumClass.getMethod(constraintAnnotation.enumFieldMethod());
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("指定的枚举字段方法不存在", e);
}
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) {
return allowNull; // 允许空值时返回 true,不允许时返回 false
}
try {
for (Enum<?> enumConstant : enumClass.getEnumConstants()) {
Object enumFieldValue = enumFieldMethod.invoke(enumConstant);
// Debug information
System.out.println("Comparing value: " + value + " with enum field value: " + enumFieldValue);
if (enumFieldValue != null && String.valueOf(enumFieldValue).equals(value)) {
return true;
}
}
return false;
} catch (Exception e) {
throw new RuntimeException("枚举验证过程中发生错误", e);
}
}
}
java
@EnumFieldValid(enumClass = StatusEnum.class, enumFieldMethod = "getCode", message = "状态码必须为1或2")
@Schema(description = "状态", allowableValues = {"1", "2"})
private String validStatus;
14、@Valid和@Validated区别
区别 | @Valid | @Validated |
---|---|---|
提供者 | JSR-303规范 | Spring |
是否支持分组 | 不支持 | 支持 |
嵌套校验 | 支持 | 不支持 |
写在最后
好了,今天关于技术探索、学习和项目难点的分享就到这里啦!我知道,很多小伙伴在技术的道路上可能会遇到各种各样的问题,有时候会觉得迷茫,有时候会想要放弃。但是我想说,别灰心,别气馁!
咱们都是从不会到会,从做不好到能做好的。就像爬山一样,有时候会觉得路途艰辛,气喘吁吁,但只要一步一个脚印地往上走,最终一定能登上山顶,看到那美丽的风景。
所以啊,不管是学习新技术,还是做个人项目,只要咱们保持那股子热情和劲头,遇到问题多琢磨琢磨,多尝试尝试,总会把问题解决的。相信自己,你就是那个未来的技术大佬!加油吧!