springboot参数校验

284 阅读3分钟

1.spring-boot-starter-validation依赖

spring-boot-starter-validation 是 Spring Boot 提供的一个 starter 依赖,它整合了验证相关的所有依赖,简化了使用过程。这个 starter 会自动引入以下两个主要依赖:

  • jakarta.validation-api:提供验证的规范和接口(如 @NotBlank@Pattern 等注解)。

  • hibernate-validator:作为验证规范的实现,执行实际的校验工作。

引入:

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

2.常用的注解

@NotNull

用于确保字段不为 null
如果字段为 null,将触发验证失败。

import javax.validation.constraints.NotNull;

public class User {
    @NotNull(message = "用户名不能为空")
    private String username;
}

@NotBlank

用于确保字段不为 null 且不为空字符串。适用于 String 类型字段,验证内容是否为空(包括空格)。

import javax.validation.constraints.NotBlank;

public class User {
    @NotBlank(message = "用户名不能为空")
    private String username;
}

@NotEmpty

@NotBlank 类似,但适用于所有类型,确保集合、数组等不为空。

import javax.validation.constraints.NotEmpty;

public class User {
    @NotEmpty(message = "邮箱不能为空")
    private List<String> emails;
}

@Size

用于验证字符串、集合、数组等的大小是否符合要求。

import javax.validation.constraints.Size;

public class User {
    @Size(min = 5, max = 20, message = "用户名长度必须在5到20个字符之间")
    private String username;
}

@Pattern

用于正则表达式验证,验证字符串是否符合给定的模式。

import javax.validation.constraints.Pattern;

public class User {
    @Pattern(regexp = "^[a-zA-Z]{6,}$", message = "用户名必须是至少6个字母")
    private String username;
}

@Min 和 @Max

用于验证数值是否在指定的最小值或最大值之间。适用于 int, long, float, double 等。

import javax.validation.constraints.Min;
import javax.validation.constraints.Max;

public class Product {
    @Min(value = 1, message = "价格必须大于等于1")
    @Max(value = 1000, message = "价格必须小于等于1000")
    private double price;
}

@Email

用于验证字段是否为有效的电子邮件地址。

import javax.validation.constraints.Email;

public class User {
    @Email(message = "请输入有效的邮箱地址")
    private String email;
}

@Future 和 @Past

用于验证日期是否在未来或过去。

import javax.validation.constraints.Future;
import java.util.Date;

public class Event {
    @Future(message = "事件日期必须是未来的日期")
    private Date eventDate;
}

@Valid

用于嵌套对象的校验。将该注解放在另一个对象字段上,表示该字段对象需要进行验证。

import javax.validation.Valid;

public class User {
    @Valid
    private Address address;
}

public class Address {
    @NotNull(message = "地址不能为空")
    private String addressLine;
}

3.使用这些注解的步骤:

  • 添加依赖
    确保项目中已经加入了 spring-boot-starter-validation 依赖。

  • 在 Bean 中使用注解
    如上面所示,你可以在 Java Bean 的字段上直接使用这些验证注解。

  • 使用 @Valid@Validated 进行校验
    在 Controller 中,你可以使用 @Valid@Validated 注解来触发验证。

import javax.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

@PostMapping("/register")
public Result register(@Valid @RequestBody User user) {
    // 注册逻辑
}
  • 处理验证错误
    你可以使用 BindingResult 来捕获验证结果,或者让 Spring 自动处理验证错误,返回 400 错误。

4.@valid和@Validated的区别和使用场景

@Valid 注解

  • @Valid 注解用于触发 对象级别的验证,例如在控制器方法中对请求参数、请求体、或方法参数进行验证。
  • @Valid 本身没有分组验证功能,它的主要作用是简单地触发验证机制。

使用场景:

  • 当你想对方法参数或字段进行简单的验证时,使用 @Valid
package com.akbar.controller;

import com.akbar.service.AdminService;
import com.akbar.util.Result;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admin")
// 因为我暂时没有分组校验的需求,所以可以只使用@valid
@Valid
public class AdminController {

    private final AdminService adminService;

    @Autowired
    public AdminController(AdminService adminService) {
        this.adminService = adminService;
    }

    // 管理员注册
    @PostMapping
    public Result<Void> register(
            @RequestParam
            @NotBlank(message = "用户名不能为空")
            @Pattern(regexp = "^[a-zA-Z]{5,}$", message = "用户名必须是至少5个字母,并且只能包含英文字母")
            String username,
            @RequestParam
            @NotBlank(message = "密码不能为空")
            @Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*\d)[A-Za-z\d]{8,}$", message = "密码必须包含字母和数字,且至少8个字符")
            String password,
            @RequestParam
            @NotBlank(message = "确认密码不能为空")
            String confirmPassword) {

        if (!password.equals(confirmPassword)) {
            return Result.error("密码和确认密码不匹配!");
        }

        boolean result = adminService.registerAdmin(username, password);
        if (!result) {
            return Result.error("用户名已存在!");
        }
        return Result.success("注册成功!");
    }
}

什么是分组校验

你可以为验证规则定义不同的分组,然后在调用时指定需要验证的分组,控制哪些验证规则应该被触发。

比如下面的代码:

package com.akbar.pojo;
import com.akbar.anno.State;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import org.hibernate.validator.constraints.URL;
import java.time.LocalDateTime;

@TableName(value = "article")
public class Article {
    @TableId(value = "id", type = IdType.AUTO)
    @NotNull(groups = Article.Update.class)   // 属于Update分组
    private Integer id;

    @TableField(value = "title")
    @NotEmpty(groups = Article.Add.class, message = "文章标题不能为空!")  // 属于Add分组
    @Pattern(regexp = "^\S{1,30}$",message = "文章标题是1到30个字符!")
    private String title;

    @TableField(value = "summary")
    private String summary;

    @TableField(value = "content")
    @NotEmpty(groups = Article.Add.class, message = "文章内容不能为空!") // 属于Add分组
    private String content;
    
    ...
    
    // 定义验证分组,定义了两个分组,分别是Add和Update
    public interface Add {

    }
    public interface Update {

    }
    /*-----------------------getter,setter---------------*/
    .....
    }

在控制器中这样使用:

//更新文章
@PutMapping
// 当Update的时候触发相应分组中的校验规则
public Result update(@RequestBody @Validated(Article.Update.class) Article article) {
    boolean updateResult = articleService.update(article);
    if (updateResult) {
        return Result.success();
    } else {
        return Result.error("该文章标题已经存在!");
    }
}
//新增文章
@PostMapping
// 当Add的时候触发相应分组中的校验规则
public Result add(@RequestBody @Validated(Article.Add.class) Article article) {
    boolean addResult = articleService.add(article);
    if (addResult) {
        return Result.success();
    } else {
        return Result.error("文章添加失败!");
    }
}

@Validated 注解

  • @ValidatedSpring Framework 提供的注解,主要用于支持 分组验证(grouped validation)。

  • 它是 Spring 中 @Valid 的增强版,提供了额外的功能,特别是支持通过 分组 来控制验证的范围。

什么时候使用 @Valid,什么时候使用 @Validated

  • 使用 @Valid

    • 如果你没有涉及到 分组验证,并且只需要进行简单的验证,使用 @Valid 就足够了。
    • 适用于不需要控制验证范围的场景,例如直接验证方法参数、请求体等。
  • 使用 @Validated

    • 如果你需要根据不同的 验证分组 来控制验证的范围,或者你希望对不同的验证场景进行不同的校验,可以使用 @Validated
    • 适用于更复杂的验证场景,例如创建时验证某些字段,更新时验证其他字段。