基于Spring Validation实现全局参数校验异常处理的示例详解

 更新时间:2025年08月26日 09:33:57   作者:小厂永远得不到的男人  
在 Spring Boot 项目开发中,接口参数校验是保障数据合法性的关键环节,Spring Validation 提供了便捷的参数校验能力,下面我们就来看看具体的实现方法吧

在 Spring Boot 项目开发中,接口参数校验是保障数据合法性的关键环节。Spring Validation 提供了便捷的参数校验能力,而全局异常处理则能统一规范校验失败的响应格式,提升接口友好性。本文将完整介绍如何整合两者,实现高效的参数校验与异常处理。

一、核心依赖引入

首先需在pom.xml(Maven)或build.gradle(Gradle)中引入 Spring Validation 核心依赖,用于支持参数校验注解。

Maven 依赖

<!-- Spring Validation 核心依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Web依赖(若项目已引入可忽略) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

二、参数校验基础使用(注解式校验)

通过在实体类字段或接口方法参数上添加校验注解,实现 “声明式” 参数校验,无需手动编写校验逻辑。

1. 实体类参数校验(常用场景)

在接收请求体的 DTO(数据传输对象)中,为字段添加@NotNull@NotBlank@Min等注解,定义校验规则。

import jakarta.validation.constraints.*; // Spring Boot 3.x使用jakarta包,2.x为javax包

public class UserDTO {
    // 主键非空
    @NotNull(message = "用户ID不能为空")
    private Long id;

    // 用户名非空且长度1-20
    @NotBlank(message = "用户名不能为空")
    @Size(min = 1, max = 20, message = "用户名长度需在1-20字符之间")
    private String username;

    // 年龄18-60岁
    @Min(value = 18, message = "年龄不能小于18岁")
    @Max(value = 60, message = "年龄不能大于60岁")
    private Integer age;

    // 邮箱格式合法
    @Email(message = "邮箱格式不正确")
    private String email;

    // getter/setter省略
}

2. 接口方法添加校验触发注解

在 Controller 接口的参数前添加@Valid(或@Validated)注解,触发 Spring Validation 的校验逻辑。

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;

@RestController
public class UserController {
    // 新增用户接口,@Valid触发UserDTO的校验
    @PostMapping("/user/add")
    public String addUser(@Valid @RequestBody UserDTO userDTO) {
        // 校验通过后执行业务逻辑
        return "用户新增成功:" + userDTO.getUsername();
    }
}

三、全局参数校验异常处理(核心)

若参数校验失败,Spring 会抛出MethodArgumentNotValidException(请求体参数校验失败)或ConstraintViolationException(路径 / 请求参数校验失败)。通过@RestControllerAdvice定义全局异常处理器,统一捕获并处理这些异常,返回标准化响应。

1. 定义标准化响应体

先创建统一的接口响应类,确保异常响应与正常响应格式一致。

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Result<T> {
    // 状态码:200成功,400参数错误,500系统错误
    private Integer code;
    // 提示信息
    private String message;
    // 响应数据(成功时返回,失败时为null)
    private T data;

    // 静态工厂方法:参数错误响应
    public static Result<Void> paramError(String message) {
        return new Result<>(400, message, null);
    }

    // 静态工厂方法:成功响应(可省略)
    public static <T> Result<T> success(T data) {
        return new Result<>(200, "success", data);
    }
}

2. 全局异常处理器实现

通过@RestControllerAdvice注解标识全局异常处理类,用@ExceptionHandler指定捕获的异常类型,编写处理逻辑。

import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import java.util.stream.Collectors;

// 全局异常处理,作用于所有@RestController
@RestControllerAdvice
public class GlobalValidationExceptionHandler {

    /**
     * 处理请求体参数校验失败(@RequestBody + @Valid)
     * 异常类型:MethodArgumentNotValidException
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Void> handleMethodArgumentNotValid(MethodArgumentNotValidException e) {
        // 提取所有字段的错误信息(如多个参数校验失败,合并提示)
        String errorMsg = e.getBindingResult().getFieldErrors().stream()
                .map(FieldError::getDefaultMessage) // 获取每个字段的自定义错误信息
                .collect(Collectors.joining(";")); // 用“;”分隔多个错误

        // 返回参数错误响应
        return Result.paramError(errorMsg);
    }

    /**
     * 处理路径参数/请求参数校验失败(如@RequestParam/@PathVariable)
     * 异常类型:ConstraintViolationException
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result<Void> handleConstraintViolation(ConstraintViolationException e) {
        // 提取参数错误信息
        String errorMsg = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining(";"));

        return Result.paramError(errorMsg);
    }
}

四、效果测试与常见场景

1. 请求体参数校验失败示例

/user/add接口发送如下请求(用户名空、年龄 17):

{
  "id": 1,
  "username": "",
  "age": 17,
  "email": "invalid-email"
}

全局异常处理器会返回响应:

{
  "code": 400,
  "message": "用户名不能为空;年龄不能小于18岁;邮箱格式不正确",
  "data": null
}

2. 路径参数校验失败示例

若接口用@PathVariable接收参数并校验:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import jakarta.validation.constraints.Min;

@GetMapping("/user/{userId}")
public String getUser(@Min(value = 1, message = "用户ID不能小于1") @PathVariable Long userId) {
    return "查询用户ID:" + userId;
}

访问/user/0时,会返回:

{
  "code": 400,
  "message": "用户ID不能小于1",
  "data": null
}

五、关键注意事项

  • 注解包路径问题:Spring Boot 3.x 版本校验注解从javax.validation.constraints迁移到jakarta.validation.constraints,引入依赖时需注意包名匹配,避免类找不到异常。
  • @Valid 与 @Validated 区别@Valid是 JSR-380 标准注解,支持嵌套校验(如 DTO 内部包含另一个需要校验的对象);@Validated是 Spring 扩展注解,支持分组校验,两者均可触发校验。
  • 分组校验扩展:若同一 DTO 在不同接口需不同校验规则(如 “新增用户” 无需 ID,“修改用户” 必须有 ID),可通过 “分组” 实现,需在注解中指定groups属性,并在@Validated中指定分组。

通过以上步骤,可实现 “注解式参数校验 + 全局异常统一处理” 的完整方案,既减少重复校验代码,又保证接口响应格式统一,大幅提升开发效率与接口健壮性。

到此这篇关于基于Spring Validation实现全局参数校验异常处理的示例详解的文章就介绍到这了,更多相关Spring Validation参数校验内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅谈Spring 解决循环依赖必须要三级缓存吗

    浅谈Spring 解决循环依赖必须要三级缓存吗

    这篇文章主要介绍了浅谈Spring 解决循环依赖必须要三级缓存吗,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • PowerJob的DatabaseMonitorAspect源码流程

    PowerJob的DatabaseMonitorAspect源码流程

    这篇文章主要为大家介绍了PowerJob的DatabaseMonitorAspect源码流程解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • 解决idea创建版本时只有Java21和Java17选项

    解决idea创建版本时只有Java21和Java17选项

    你是否在使用IntelliJ IDEA创建新项目时遇到了只有Java 21和Java 17的选项?别担心,我们的指南将为你提供解决方案,通过简单的步骤,你将能够选择你需要的任何Java版本,继续阅读,让我们开始吧!
    2024-03-03
  • SpringCloud整合OpenFeign问题

    SpringCloud整合OpenFeign问题

    这篇文章主要介绍了SpringCloud整合OpenFeign问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • Java中volatile 的作用

    Java中volatile 的作用

    这篇文章主要介绍了Java中volatile 的作用,volatile是Java并发编程的重要组成部分,主要作用是保证内存的可见性和禁止指令重排序,下文更多对volatile作用的介绍,需要的小伙伴可以参考一下
    2022-05-05
  • SpringBoot利用Junit动态代理实现Mock方法

    SpringBoot利用Junit动态代理实现Mock方法

    说到Spring Boot 单元测试主要有两个主流集成分别是Mockito,Junit,这个各有特点,在实际开发中,我想要的测试框架应该是这个框架集成者,本文给大家介绍了SpringBoot利用Junit动态代理实现Mock方法,需要的朋友可以参考下
    2024-04-04
  • Spring MVC+mybatis实现注册登录功能

    Spring MVC+mybatis实现注册登录功能

    这篇文章主要为大家详细介绍了Spring MVC+mybatis实现注册登录功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • SpringBoot中@RestControllerAdvice 全局异常处理的实现

    SpringBoot中@RestControllerAdvice 全局异常处理的实现

    本文主要介绍了SpringBoot中@RestControllerAdvice 全局异常处理的实现,通过定义统一响应格式、自定义异常类及测试验证,确保接口异常时返回指定格式的提示信息,提升错误处理一致性
    2025-06-06
  • 教你使用eclipse 搭建Swt 环境的全过程

    教你使用eclipse 搭建Swt 环境的全过程

    本文给大家分享使用eclipse 搭建Swt 环境的全过程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • SpringBoot解决BigDecimal传到前端后精度丢失问题

    SpringBoot解决BigDecimal传到前端后精度丢失问题

    这篇文章将通过示例详细为大家介绍SpringBoot如何解决BigDecimal传到前端后精度丢失问题,文中的示例代码讲解详细,感兴趣的可以了解一下
    2022-06-06

最新评论