java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot validation校验

SpringBoot利用validation实现数据校验完整指南

作者:Micro麦可乐

Spring Boot 提供的 Validation(基于 JSR 303/380 规范)让我们能通过注解的方式优雅地完成参数校验,极大地提升了开发效率和代码可读性,下面我们就来看看具体实现吧

1. 前言

在我们日常开发中,后端经常需要对请求参数进行校验。比如注册用户时,用户名不能为空、密码长度要在 6~16 之间、邮箱必须符合格式等等,如果我们不做校验,脏数据就可能进入数据库,造成业务问题;如果校验方式不合理,代码又会变得臃肿

相信很多小伙伴还在 Controller 代码中写大量重复的 if-else 判断,既冗余又难维护!(如果你也是这样操作,那一定要看完本篇文章)

下面博主介绍一下Spring Boot 提供的 Validation(基于 JSR 303/380 规范)让我们能通过注解的方式优雅地完成参数校验,极大地提升了开发效率和代码可读性

2. 没有使用 Validation 的传统写法

下面我们先看看没用 Validation 的“土法校验”,再对比一下用了注解后的优雅写法。当不使用数据校验框架时,我们通常会在 Controller 中手动校验参数,代码会像这样:

场景:创建用户接口

定义接受参数对象UserDto

// UserDTO实体类
class UserDto {
    private String name;
    private Integer age;
    private String email;
    
    // getter和setter省略
}

要求:用户名不能为空,长度5-10;邮箱格式必须正确;年龄在18-60之间

@RestController
@RequestMapping("/user")
public class UserController {
    
    @PostMapping("/add")
    public String addUser(UserDto user) {
        // 手动校验参数
        if (user.getName() == null || user.getName().trim().isEmpty()) {
            return "用户名不能为空";
        }
        if (user.getName().length() < 5 || user.getName().length() > 10) {
            return "用户名长度必须在5-10之间";
        }
        if (user.getAge() == null) {
            return "年龄不能为空";
        }
        if (user.getAge() < 18 || user.getAge() > 60) {
            return "年龄必须在18-60之间";
        }
        if (user.getEmail() == null || user.getEmail().trim().isEmpty()) {
            return "邮箱不能为空";
        }
        if (!user.getEmail().matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$")) {
            return "邮箱格式不正确";
        }
        
        // 业务逻辑处理
        return "用户添加成功";
    }
}

可以看出上述写法的缺点:

3. 使用 Validation 的优雅写法

我们可以在实体类上加注解,把校验规则声明在模型上,让 Spring 自动完成校验

Maven 依赖:

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

UserDto对象加上注解

import javax.validation.constraints.*;

public class UserDto {

    @NotBlank(message = "用户名不能为空")
    @Size(min = 2, max = 10, message = "用户名长度必须在{min}-{max}之间")
    private String username;

    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确") // 自带邮箱格式校验,无需自己写正则!
    private String email;

    @NotNull(message = "年龄不能为空")
    @Min(value = 0, message = "年龄最小为{value}")
    @Max(value = 150, message = "年龄最大为{value}")
    private Integer age;

    // 省略 Getter 和 Setter...
}

在Controller参数前加@Valid或@Validated注解

import javax.validation.Valid;

@RestController
@RequestMapping("/user")
public class UserController {

    @PostMapping("/add")
    // 关键一步:在 @RequestBody 前加上 @Valid 注解
    public String addUser(@Valid @RequestBody UserDto user) {
        // 只需关注核心业务
        System.out.println("用户创建成功: " + user);
        return "success";
    }
}

通过上述使用 validation 改造,Spring 会自动对 UserDto 的字段进行校验,当请求参数不满足规则时,Spring Boot 会自动抛出 MethodArgumentNotValidException 异常,不会进入这个方法体。但我们不能直接给用户返回异常栈,需要统一处理

4. 全局异常处理(友好返回错误信息)

刚才我们已经说过了参数校验不满足规则,系统会抛出MethodArgumentNotValidException ,那么我们就可以通过 @RestControllerAdvice 捕获 MethodArgumentNotValidException,来实现统一返回错误信息

import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理实体校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String, Object> handleValidException(MethodArgumentNotValidException e) {
        Map<String, Object> errorResult = new HashMap<>();
        errorResult.put("code", 400);
        errorResult.put("message", "参数校验失败");
        // 从异常对象中拿到具体的错误信息
        // 这里只取第一个错误信息,也可以全部返回
        String defaultMessage = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
        errorResult.put("data", defaultMessage);
        return errorResult;
    }
}

最后我们可以使用Postman或curl测试,观察接口返回的JSON异常数据

5. 常用校验注解

注解功能说明
@NotNull值不能为null
@NotBlank字符串不能为空(trim后长度>0)
@NotEmpty集合、数组、Map、String不能为空
@Size(min=, max=)检查字符串、集合、数组大小
@Min(value)数字最小值
@Max(value)数字最大值
@Email校验邮箱格式
@Pattern(regexp=)正则表达式匹配
@Positive正数
@Future日期必须在未来
@Past日期必须在过去

6. 分组校验

当同一个实体类在不同场景下有不同的校验规则时,比如新增时ID应为空,而更新时ID不能为空,这时就需要分组校验

定义分组接口(标记接口)

public interface CreateGroup {} // 创建分组
public interface UpdateGroup {} // 更新分组

在实体上指定分组
继续改造一下我们的UserDto,这时候需要增加id字段

public class UserDto {
    @Null(groups = CreateGroup.class, message = "创建时ID必须为空")
    @NotNull(groups = UpdateGroup.class, message = "更新时ID不能为空")
    private Long id;

    @NotBlank(message = "用户名不能为空", groups = {CreateGroup.class, UpdateGroup.class})
    private String username;
    // ... 其他字段
}

在Controller中使用@Validated指定分组

@PostMapping("/create")
public String create(@Validated(CreateGroup.class) @RequestBody UserDto user) {
    // ... 创建逻辑
}

@PostMapping("/update")
public String update(@Validated(UpdateGroup.class) @RequestBody UserDto user) {
    // ... 更新逻辑
}

7. 结语

通过使用 Spring Boot Validation,我们可以告别繁琐的手动参数校验,让代码更加简洁、优雅、易维护。希望本文能帮助你在项目中更好地应用数据校验机制,提升开发效率和代码质量,是开发中必不可少的利器!

到此这篇关于SpringBoot利用validation实现数据校验完整指南的文章就介绍到这了,更多相关SpringBoot validation校验内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文