Spring Boot Validation 接口校验:从零到掌握

news/2025/2/23 17:44:49

在开发 Web 应用时,数据校验是不可忽视的一部分。无论是注册用户信息、提交表单数据,还是处理业务逻辑,数据的有效性和完整性都需要得到保证。Spring Boot 提供了强大的验证功能,基于 Hibernate Validator 框架,通过注解方式简化了数据校验的实现。本文将详细介绍 Spring Boot 的 Validation 接口校验机制,包括其核心功能、常用注解、自定义校验、以及实际应用场景。


1. 什么是 Spring Validation?

Spring Validation 是一个用于数据校验的框架,它基于 JSR-303(Bean Validation API) 和 Hibernate Validator 实现。通过在 JavaBean 的字段上添加特定的注解,可以定义数据的校验规则。Spring Boot 通过整合 Hibernate Validator,使得在 Web 应用中使用数据校验变得更加简单。


2. Spring Boot Validation 的核心功能

  1. 注解式校验:通过注解定义数据校验规则。
  2. 自动化校验:Spring Boot 提供了对校验的自动支持,无需手动编写校验逻辑。
  3. 异常处理:Spring Boot 可以自动将校验失败的错误信息返回给客户端。
  4. 支持分组校验:可以为不同的场景定义不同的校验分组。
  5. 支持自定义校验:可以扩展注解,定义自定义的校验逻辑。

3. 常用的校验注解

以下是 Spring Boot 中常用的校验注解:

注解功能描述
@NotNull确保字段不为 null
@Null确保字段为 null
@NotBlank确保字段不为空(字符串)
@NotEmpty确保字段不为空(集合、数组)
@Length确保字段的长度在指定范围内
@Size确保字段的长度在指定范围内(适用于集合、数组、字符串)
@Range确保字段的值在指定范围内
@Min确保字段的值大于等于指定值
@Max确保字段的值小于等于指定值
@Email确保字段为有效的电子邮件地址
@Pattern确保字段的值匹配指定的正则表达式
@Past确保字段的值是过去的日期
@Future确保字段的值是未来的日期

4. Spring Boot Validation 的实现步骤

步骤 1:添加依赖

在 pom.xml 文件中添加以下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

步骤 2:创建 JavaBean

创建一个需要校验的 JavaBean 类,并在字段上添加校验注解:

java">import jakarta.validation.constraints.*;

public class User {
    
    @NotNull(message = "用户名不能为空")
    @Size(min = 2, max = 10, message = "用户名长度必须在2到10之间")
    private String username;
    
    @NotNull(message = "密码不能为空")
    @NotBlank(message = "密码不能为空")
    @Pattern(regexp = "^(?=.*\\d)(?=.*[A-Za-z])(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,20}$", message = "密码格式不正确")
    private String password;
    
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @Min(value = 18, message = "年龄必须大于等于18岁")
    private Integer age;
    
    public User() {}
    
    // Getters and Setters
}

步骤 3:在控制器中使用 @Valid 注解

在控制器的参数中使用 @Valid 注解启用校验:

java">import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;

@RestController
public class UserController {
    
    @PostMapping("/register")
    public ResponseEntity<?> register(@Valid @RequestBody User user) {
        // 业务逻辑
        return ResponseEntity.ok("注册成功");
    }
}

步骤 4:处理校验异常

Spring Boot 会自动将校验失败的错误信息封装到 MethodArgumentNotValidException 异常中。可以通过全局异常处理来统一返回错误信息:

java">import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
    }
}

5. 自定义校验注解

如果内置的校验注解无法满足需求,可以通过自定义注解来扩展校验功能。

自定义校验注解

java">import jakarta.validation.Constraint;
import jakarta.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.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Phone {
    
    String message() default "手机号格式不正确";
    
    Class<?>[] groups() default {};
    
    Class<? extends Payload>[] payload() default {};
}

自定义校验逻辑

java">import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class PhoneValidator implements ConstraintValidator<Phone, String> {
    
    @Override
    public void initialize(Phone constraintAnnotation) {
    }
    
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return false;
        }
        // 手机号正则表达式
        String regex = "^1(3\\d|5[i-o]\\d|78\\d|4\\d)\\d{7}$";
        return value.matches(regex);
    }
}

使用自定义校验注解

java">@Phone(message = "手机号格式不正确")
private String phone;

6. 分组校验和条件校验

分组校验

通过分组校验,可以为不同的场景定义不同的校验规则。

java">public interface SaveGroup {
}

public interface UpdateGroup {
}

@NotNull(groups = SaveGroup.class)
@Size(min = 2, max = 10, groups = {SaveGroup.class, UpdateGroup.class})
private String username;

在控制器中指定需要校验的分组:

java">@PostMapping("/save")
public ResponseEntity<?> save(@Validated(SaveGroup.class) @RequestBody User user) {
    // 业务逻辑
    return ResponseEntity.ok("保存成功");
}

条件校验

通过 @ScriptAssert 注解,可以基于脚本语言(如 JavaScript 或 Groovy)实现复杂的条件校验。

java">@ScriptAssert(lang = "javascript", script = "password.length >= 8 && password.match(/^(?=.*\\d)(?=.*[A-Za-z])(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,20}$/)")
public class User {
    // 字段定义
}

7. 结合其他技术

1. 统一异常处理

通过全局异常处理,可以统一返回校验失败的错误信息,提升用户体验。

2. 日志记录

通过 AOP(Aspect Oriented Programming),可以记录校验失败的日志,方便后续分析:

java">@Aspect
@Component
public class ValidationAspect {
    
    @Around("execution(* *(..)) && @annotation(org.springframework.web.bind.annotation.PostMapping)")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            Object[] args = joinPoint.getArgs();
            if (args != null && args.length > 0) {
                for (Object arg : args) {
                    if (arg != null && arg.getClass().getAnnotation(Valid.class) != null) {
                        // 记录日志
                        System.out.println("开始校验数据:" + arg);
                    }
                }
            }
            return joinPoint.proceed();
        } catch (MethodArgumentNotValidException ex) {
            // 记录校验失败的日志
            System.out.println("校验失败:" + ex.getBindingResult());
            throw ex;
        }
    }
}

8. 常见问题和解决方案

常见问题

  1. 校验注解不生效
    • 检查是否添加了 spring-boot-starter-validation 依赖。
    • 确保在控制器中使用了 @Valid 注解。
  2. 错误信息不返回
    • 检查是否实现了全局异常处理。
    • 确保控制器的返回类型为 ResponseEntity
  3. 自定义校验注解不生效
    • 检查自定义注解的 ConstraintValidator 是否正确实现。
    • 确保自定义注解使用了 @Constraint 注解。

总结

Spring Boot 的 Validation 功能提供了一种简单而强大的数据校验方式,通过注解式校验和自动化处理,能够显著提升开发效率和代码质量。结合 Hibernate Validator 的强大功能,开发者可以轻松实现复杂的校验逻辑,同时通过自定义校验注解和分组校验,满足不同的业务需求。

希望本文能帮助你在实际项目中更好地使用 Spring Boot 的 Validation 功能,提高代码的健壮性和用户体验!


http://www.niftyadmin.cn/n/5863631.html

相关文章

11.Docker 之分布式仓库 Harbor

Docker 之分布式仓库 Harbor Docker 之分布式仓库 Harbor1. Harbor 组成2. 安装 Harbor Docker 之分布式仓库 Harbor Harbor 是一个用于存储和分发 Docker 镜像的企业级 Registry 服务器&#xff0c;由 VMware 开源&#xff0c;其通过添加一些企业必需的功能特性&#xff0c;例…

Spring Boot 集成 T-io 实现客户端服务器通信

Spring Boot 集成 T-io 实现客户端服务器通信 本文详细介绍如何在 Spring Boot 项目中集成 T-io 框架&#xff0c;实现客户端与服务器之间的通信&#xff0c;并支持组聊、群聊和私聊功能。通过本文&#xff0c;您能够全面了解 T-io core 的使用方法&#xff0c;以及如何正确启…

从零开始学 Rust:基本概念——变量、数据类型、函数、控制流

文章目录 Variables and MutabilityShadowing Data TypesScalar TypesCompound Types FunctionsFunction Parameters CommentsControl FlowRepetition with Loops Variables and Mutability fn main() {let mut x 5;println!("The value of x is: {}", x);x 6;pri…

蓝桥备赛(一)- C++入门(上)

一、工具安装 Dev-C安装&#xff1a;https://www.bilibili.com/video/BV1kC411G7CS 一般比赛会用到Dev-C, 但是Dev-C还是有自身的局限性 &#xff0c; 后续的博客学习中 &#xff0c; 必要的时候 &#xff0c; 会使用VS2022 &#xff0c; 下面是VS2022的安装和使用教程。 VS202…

Go语言中使用viper绑定结构体和yaml文件信息时,标签的使用

在Go中使用Viper将YAML配置绑定到结构体时&#xff0c;主要依赖 mapstructure 标签&#xff08;而非 json 或 yaml 标签&#xff09;实现字段名映射。 --- ### 1. **基础绑定方法** 使用 viper.Unmarshal(&config) 或 viper.UnmarshalKey("key", &subConfi…

基于FISCO-BCOS搭建第一个区块链网络

一、前言介绍&#xff1a; 本篇博客以Ubuntu虚拟机为例 本篇博客我会大致介绍“搭建第一个区块链网络”的搭建过程&#xff0c;具体的还是要查看FISCO-BCOS的官方文档。会着重介绍在搭建过程中可能遇到的一些报错&#xff0c;以及解决报错的常用方法。 参考FISCO-BCOS的官方文档…

Android14 Camera框架中Jpeg流buffer大小的计算

背景描述 Android13中&#xff0c;相机框架包含对AIDL Camera HAL的支持&#xff0c;在Android13或更高版本中添加的相机功能只能通过AIDL Camera HAL接口使用。 对于Android应用层来说&#xff0c;使用API34即以后版本的Camera应用程序通过Camera AIDL Interface访问到HAL层…

TCP/UDP调试工具推荐:Socket通信图解教程

TCP/UDP调试工具推荐&#xff1a;Socket通信图解教程 一、引言二、串口调试流程三、下载链接 SocketTool 调试助手是一款旨在协助程序员和网络管理员进行TCP和UDP协议调试的网络通信工具。TCP作为一种面向连接、可靠的协议&#xff0c;具有诸如连接管理、数据分片与重组、流量和…