🎓博主介绍:Java、Python、js全栈开发 “多面手”,精通多种编程语言和技术,痴迷于人工智能领域。秉持着对技术的热爱与执着,持续探索创新,愿在此分享交流和学习,与大家共进步。
📖DeepSeek-行业融合之万象视界(附实战案例详解100+)
📖全栈开发环境搭建运行攻略:多语言一站式指南(环境搭建+运行+调试+发布+保姆级详解)
👉感兴趣的可以先收藏起来,希望帮助更多的人
SpringBoot 参数校验终极指南:Validation+自定义校验器
一、引言
在开发 Spring Boot 应用程序时,参数校验是一个非常重要的环节。有效的参数校验可以保证系统的稳定性和安全性,防止因无效参数导致的各种异常和错误。Spring Boot 提供了强大的参数校验机制,结合 Validation 框架和自定义校验器,能够满足各种复杂的校验需求。本文将详细介绍如何在 Spring Boot 中使用 Validation 框架进行参数校验,并通过自定义校验器来实现更灵活的校验逻辑。
二、Spring Boot 中使用 Validation 框架基础
2.1 添加依赖
首先,我们需要在 pom.xml
文件中添加 Spring Boot Validation 相关的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.2 基本校验注解的使用
Validation 框架提供了一系列的基本校验注解,例如 @NotNull
、@NotEmpty
、@NotBlank
、@Size
、@Min
、@Max
等。下面是一个简单的示例:
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
public class User {
@NotNull(message = "用户 ID 不能为空")
private Long id;
@NotBlank(message = "用户名不能为空")
private String username;
@Min(value = 18, message = "年龄不能小于 18 岁")
@Max(value = 100, message = "年龄不能大于 100 岁")
private Integer age;
// 省略 getter 和 setter 方法
}
2.3 在 Controller 中启用校验
在 Controller 方法的参数前添加 @Valid
注解,即可启用对该参数的校验:
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
public class UserController {
@PostMapping("/users")
public String createUser(@Valid @RequestBody User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return bindingResult.getAllErrors().get(0).getDefaultMessage();
}
// 处理业务逻辑
return "用户创建成功";
}
}
三、自定义校验注解
3.1 自定义校验注解的需求场景
在实际开发中,基本的校验注解可能无法满足所有的需求。例如,我们需要校验一个字符串是否为合法的邮箱地址,或者校验一个日期是否在某个特定的范围内。这时,就需要自定义校验注解。
3.2 自定义校验注解的实现步骤
3.2.1 定义校验注解
首先,我们需要定义一个自定义的校验注解。以下是一个简单的示例,用于校验字符串是否为合法的手机号:
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Constraint(validatedBy = PhoneValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Phone {
String message() default "手机号格式不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
3.2.2 实现校验器
接下来,我们需要实现一个校验器类,用于具体的校验逻辑:
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Pattern;
public class PhoneValidator implements ConstraintValidator<Phone, String> {
private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
@Override
public void initialize(Phone constraintAnnotation) {
// 初始化方法,可用于获取注解中的属性值
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
return PHONE_PATTERN.matcher(value).matches();
}
}
3.2.3 使用自定义校验注解
在需要校验的字段上添加自定义校验注解:
import javax.validation.constraints.NotNull;
public class User {
@NotNull(message = "用户 ID 不能为空")
private Long id;
@Phone
private String phone;
// 省略 getter 和 setter 方法
}
四、分组校验
4.1 分组校验的概念
在某些情况下,我们可能需要根据不同的业务场景对参数进行不同的校验。例如,在用户注册时,需要校验用户名和密码;而在用户修改信息时,可能只需要校验部分信息。这时,就可以使用分组校验。
4.2 分组校验的实现步骤
4.2.1 定义分组接口
public interface CreateGroup {}
public interface UpdateGroup {}
4.2.2 在校验注解中指定分组
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
public class User {
@NotNull(message = "用户 ID 不能为空", groups = UpdateGroup.class)
private Long id;
@NotBlank(message = "用户名不能为空", groups = {CreateGroup.class, UpdateGroup.class})
private String username;
// 省略 getter 和 setter 方法
}
4.2.3 在 Controller 中指定分组
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
public class UserController {
@PostMapping("/users")
public String createUser(@Valid(groups = CreateGroup.class) @RequestBody User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return bindingResult.getAllErrors().get(0).getDefaultMessage();
}
// 处理业务逻辑
return "用户创建成功";
}
@PostMapping("/users/{id}")
public String updateUser(@Valid(groups = UpdateGroup.class) @RequestBody User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return bindingResult.getAllErrors().get(0).getDefaultMessage();
}
// 处理业务逻辑
return "用户信息更新成功";
}
}
五、全局异常处理
5.1 全局异常处理的作用
当参数校验失败时,会抛出 MethodArgumentNotValidException
异常。为了统一处理这些异常,我们可以使用全局异常处理器。
5.2 实现全局异常处理器
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
BindingResult bindingResult = ex.getBindingResult();
Map<String, String> errors = new HashMap<>();
bindingResult.getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return errors;
}
}
六、总结
通过本文的介绍,我们学习了如何在 Spring Boot 中使用 Validation 框架进行参数校验,以及如何通过自定义校验器实现更灵活的校验逻辑。同时,我们还了解了分组校验和全局异常处理的使用方法。这些技术可以帮助我们在开发过程中更好地保证系统的稳定性和安全性。