当前位置:首页 > 文章列表 > 文章 > java教程 > Spring Boot 参数校验工作流:DTO、注解和统一错误响应

Spring Boot 参数校验工作流:DTO、注解和统一错误响应

来源:17golang原创 2026-06-27 19:27:09 0浏览 收藏

Java 后端接口最容易混乱的地方之一,是参数校验散落在控制器、服务层和数据库异常里。短期看只是多写几个 if,长期会变成错误信息不一致、前端无法定位字段、测试用例难覆盖。

本文把 Spring Boot 参数校验整理成一个完整工作流:先明确接口边界,再用 DTO 承接输入,用注解声明规则,用 @Valid 触发校验,最后通过统一异常处理返回稳定的错误结构。

目录
  • 目标和边界:校验应该解决什么问题
  • 全流程总览:从请求体到错误响应
  • 阶段一:用 DTO 明确输入边界
  • 阶段二:用注解声明字段规则
  • 阶段三:统一错误响应结构
  • 推荐流程:从新增接口到回归检查
  • 常见误区和速查表

目标和边界:校验应该解决什么问题

参数校验的目标不是把所有业务规则都塞进注解,而是先拦住明显不合法的输入,让控制器收到的数据满足基本形状。例如用户名不能为空、手机号格式不对、页码不能小于 1、金额不能为负数。

建议把规则分成三类:

  • 结构规则:字段是否必填、长度范围、数值范围、集合大小。
  • 格式规则:邮箱、手机号、日期字符串、枚举值。
  • 业务规则:库存是否足够、用户是否有权限、订单状态是否允许变更。

前两类适合放在 DTO 校验里,第三类通常放在服务层。边界清楚后,代码会更稳定。

全流程总览:从请求体到错误响应

一个推荐的参数校验链路可以拆成五步:请求进入控制器、绑定到 DTO、触发注解校验、捕获字段错误、返回统一响应。这样每个接口都沿用同一套入口和错误格式。

Spring Boot 参数校验工作流,从请求体、DTO、注解校验、错误收集到统一响应
图 1:参数校验工作流把请求输入、字段规则和错误响应串成一条稳定链路。

阶段一:用 DTO 明确输入边界

不要直接把数据库实体当作请求参数对象。实体通常包含主键、创建时间、内部状态等字段,直接暴露给接口会让输入边界变模糊。推荐为每个写接口准备单独的请求 DTO。

public class CreateUserRequest {

    private String username;

    private String phone;

    private Integer age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

DTO 的职责是描述外部输入。它不负责保存数据,也不负责判断复杂业务状态。

阶段二:用注解声明字段规则

Spring Boot 常用 Bean Validation 注解声明字段约束。不同版本项目可能使用 jakarta.validation 或 javax.validation 包,原则相同:规则写在 DTO 字段上,由框架在控制器入口触发。

import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;

public class CreateUserRequest {

    @NotBlank(message = "用户名不能为空")
    @Size(max = 32, message = "用户名不能超过32个字符")
    private String username;

    @NotBlank(message = "手机号不能为空")
    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String phone;

    @Min(value = 1, message = "年龄不能小于1")
    @Max(value = 120, message = "年龄不能大于120")
    private Integer age;

    // getter 和 setter 省略
}

控制器入口需要加上 @Valid,否则注解只是一组元数据,不会自动触发校验。

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

@RestController
public class UserController {

    @PostMapping("/users")
    public ApiResult createUser(@Valid @RequestBody CreateUserRequest request) {
        Long userId = 1001L;
        return ApiResult.ok(userId);
    }
}

阶段三:统一错误响应结构

校验失败时,前端最需要知道两个信息:哪个字段错了,以及错误原因是什么。不要只返回一句“参数错误”,否则前端无法高亮具体输入框。

Spring Boot 参数校验错误响应路径,从字段错误到 errors 数组和统一响应体
图 2:统一错误响应应保留字段名、错误消息和稳定错误码。
import java.util.List;

public class ApiResult {

    private String code;
    private String message;
    private T data;

    public static  ApiResult ok(T data) {
        ApiResult result = new ApiResult();
        result.code = "OK";
        result.message = "success";
        result.data = data;
        return result;
    }

    public static ApiResult> badRequest(List errors) {
        ApiResult> result = new ApiResult();
        result.code = "BAD_REQUEST";
        result.message = "参数校验失败";
        result.data = errors;
        return result;
    }
}
public record FieldErrorItem(String field, String message) {
}

统一异常处理可以集中收集字段错误:

import java.util.List;

import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalErrorHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResult> handleInvalidBody(
            MethodArgumentNotValidException ex) {

        List errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(error -> new FieldErrorItem(error.getField(), error.getDefaultMessage()))
                .toList();

        return ApiResult.badRequest(errors);
    }
}

返回结构可以保持稳定:

{
  "code": "BAD_REQUEST",
  "message": "参数校验失败",
  "data": [
    {
      "field": "phone",
      "message": "手机号格式不正确"
    }
  ]
}

推荐流程:从新增接口到回归检查

新增一个写接口时,可以按下面的顺序推进:

  1. 先定义请求 DTO,只放外部允许传入的字段。
  2. 给必填、长度、范围、格式规则添加注解。
  3. 控制器入口添加 @Valid 和 @RequestBody。
  4. 统一异常处理里返回字段级错误列表。
  5. 为必填为空、格式错误、边界值写接口测试。
  6. 把跨用户、权限、库存这类业务规则留在服务层。

常见误区和速查表

误区 1:DTO 和实体混用

实体字段通常比接口输入更多,混用会扩大可写范围。DTO 应该面向接口设计,实体面向存储设计。

误区 2:忘记添加 @Valid

字段上写了注解,但控制器入口没有 @Valid,校验不会按预期触发。排查时先看控制器方法签名。

误区 3:把复杂业务规则写进注解

库存、权限、订单状态这类规则依赖数据库或上下文,放在服务层更清晰。DTO 注解适合处理输入形状和基础格式。

速查表

目标 常用做法 检查点
字段必填 @NotBlank、@NotNull 空字符串和 null 都要测试
长度范围 @Size 测试最大长度边界
数值范围 @Min、@Max 测试最小值和最大值
格式规则 @Pattern 错误样例要覆盖常见输入
错误响应 统一异常处理 返回字段名和错误消息

总结一下,参数校验要形成工作流,而不是散落的条件判断。DTO 控制输入边界,注解声明基础规则,控制器触发校验,统一异常处理稳定错误响应。这样新接口越多,参数校验越容易复用和回归。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
MySQL 覆盖索引实验:从慢查询到 EXPLAIN 显示 Using indexMySQL 覆盖索引实验:从慢查询到 EXPLAIN 显示 Using index
上一篇
MySQL 覆盖索引实验:从慢查询到 EXPLAIN 显示 Using index
RAG 答非所问怎么排查:从切块、向量到召回上下文
下一篇
RAG 答非所问怎么排查:从切块、向量到召回上下文
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    258次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    302次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    281次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    259次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    67次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码