一、核心说明
本文整理了SpringBoot项目开发中,API参数校验最核心的三项实战能力,均为企业项目通用落地方案。内容精简实用,无冗余理论,包含可直接上线使用的完整代码、开发规则、易错点及统一返回规范,适配日常开发、功能迭代及接口标准化整改场景。
三大核心:请求体Body自动化校验、查询参数Query自动化校验、全局统一错误返回格式
1.必备前置依赖 SpringBoot2.3+ 必须手动引入,提供所有校验注解能力
js
<?xml version="1.0" encoding="UTF-8"?>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>
二、请求体(Body)参数校验【POST/PUT】
- 核心规则 主要用于POST、PUT请求的复杂入参场景,支持对象、嵌套对象、集合类参数校验。通过@Valid注解开启自动化校验,覆盖全部基础校验规则,开发中无需手写大量if判断,代码更简洁规范。
- 核心校验注解区分(必记)
- @NotNull:数值、对象非空
- @NotBlank:字符串非空
- @NotEmpty:集合、字符串非空
- @Min/@Max:数值范围限制
- @Size:字符串/集合长度限制
- @Pattern:正则格式校验
3.核心DTO代码
pyton
from dataclasses import dataclass
from typing import List, Optional
from decimal import Decimal
import re
class ValidationError(Exception):
def __init__(self, field, message):
self.field = field
self.message = message
super().__init__(f"{field}: {message}")
@dataclass
class OrderItemDTO:
goods_id: int
count: int
def validate(self):
errors = []
if self.goods_id is None:
errors.append(ValidationError("goodsId", "商品ID不能为空"))
elif self.goods_id < 1:
errors.append(ValidationError("goodsId", "商品ID非法"))
if self.count is None:
errors.append(ValidationError("count", "购买数量不能为空"))
elif self.count < 1:
errors.append(ValidationError("count", "数量不能小于1"))
if errors:
raise ValueError("; ".join([f"{e.field}: {e.message}" for e in errors]))
@dataclass
class OrderCreateDTO:
user_id: int
phone: str
amount: Decimal
item_list: List[OrderItemDTO]
remark: Optional[str] = None
def validate(self):
errors = []
# 用户ID校验
if self.user_id is None:
errors.append(ValidationError("userId", "用户ID不能为空"))
elif self.user_id < 1:
errors.append(ValidationError("userId", "用户ID取值非法"))
# 手机号校验
if not self.phone or not self.phone.strip():
errors.append(ValidationError("phone", "手机号不能为空"))
elif not re.match(r"^1[3-9]\d{9}$", self.phone):
errors.append(ValidationError("phone", "手机号格式错误"))
# 订单金额校验
if self.amount is None:
errors.append(ValidationError("amount", "订单金额必须大于0"))
elif self.amount <= 0:
errors.append(ValidationError("amount", "订单金额必须大于0"))
# 备注长度校验
if self.remark and len(self.remark) > 500:
errors.append(ValidationError("remark", "备注不能超过500字符"))
# 商品列表校验
if not self.item_list:
errors.append(ValidationError("itemList", "商品列表不能为空"))
else:
for i, item in enumerate(self.item_list):
try:
item.validate()
except ValueError as e:
errors.append(ValidationError(f"itemList[{i}]", str(e)))
if errors:
raise ValueError("; ".join([f"{e.field}: {e.message}" for e in errors]))
# 使用示例
if __name__ == "__main__":
# 创建订单项目
item1 = OrderItemDTO(goods_id=123, count=2)
item2 = OrderItemDTO(goods_id=456, count=1)
# 创建订单
order = OrderCreateDTO(
user_id=1001,
phone="13812345678",
amount=Decimal("99.99"),
item_list=[item1, item2],
remark="请尽快发货"
)
try:
order.validate()
print("订单验证通过")
except ValueError as e:
print(f"验证失败: {e}")
- Controller开启校验
java
package com.example.controller;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequestMapping("/api/order")
public class OrderController {
@PostMapping("/create")
public ResultVO<String> createOrder(@Valid @RequestBody OrderCreateDTO orderDTO) {
return ResultVO.success("订单创建成功");
}
}```
开发高频易错点:如果参数中存在嵌套对象、集合嵌套,仅在顶层参数加@Valid无法生效,嵌套层级必须单独添加@Valid注解,否则子参数校验失效,出现参数穿透问题
# 三、查询参数(Query)校验
适用于GET请求的分页查询、条件筛选、排序等简单参数场景,是查询参数专属校验方式。和请求体校验用法有明显区别,不需要在方法参数上加@Valid
**1.分页参数DTO**
```python
from dataclasses import dataclass
from typing import Optional
class ValidationError(Exception):
def __init__(self, field, message):
self.field = field
self.message = message
super().__init__(f"{field}: {message}")
@dataclass
class PageQueryDTO:
pageNum: int = 1
pageSize: int = 10
sortField: Optional[str] = None
sortType: Optional[str] = None
def validate(self):
errors = []
# 页码校验
if self.pageNum is None or self.pageNum < 1:
errors.append(ValidationError("pageNum", "页码不能小于1"))
# 每页大小校验
if self.pageSize is None or self.pageSize < 1:
errors.append(ValidationError("pageSize", "每页大小不能小于1"))
elif self.pageSize > 100:
errors.append(ValidationError("pageSize", "每页最大100条,防止全表查询"))
# 排序字段校验
if self.sortField:
import re
if not re.match(r"^[a-zA-Z0-9_]{1,30}$", self.sortField):
errors.append(ValidationError("sortField", "排序字段非法"))
# 排序方式校验
if self.sortType:
if self.sortType.lower() not in ["asc", "desc"]:
errors.append(ValidationError("sortType", "排序方式仅支持asc/desc"))
if errors:
raise ValueError("; ".join([f"{e.field}: {e.message}" for e in errors]))```
**2.Controller开启校验**
```python
// 仅需在类上加@Validated,全局开启Query/路径参数校验
@Validated
@RestController
@RequestMapping("/api/user")
public class UserController {
@GetMapping("/page")
public ResultVO<Object> getUserPage(PageQueryDTO pageQueryDTO) {
return ResultVO.success("查询成功", null);
}
}
四、全局统一错误返回格式
项目原生的参数校验异常会直接抛出堆栈信息,前端无法解析、页面展示异常,还会暴露项目结构。通过全局统一异常处理,可统一接口返回格式,屏蔽错误堆栈,提示信息精准友好,适配前后端联调规范 1.统一返回实体ResultVO
python
from typing import TypeVar, Generic, Optional
T = TypeVar('T')
class ResultVO(Generic[T]):
"""API结果封装类"""
def __init__(self, code: int = None, msg: str = None, data: T = None):
self.code = code
self.msg = msg
self.data = data
@classmethod
def success(cls, msg: str, data: T = None):
"""成功响应"""
result = cls()
result.code = 200
result.msg = msg
result.data = data
return result
@classmethod
def success_simple(cls, msg: str):
"""成功响应(无数据)"""
return cls.success(msg, None)
@classmethod
def param_error(cls, msg: str):
"""参数错误响应"""
result = cls()
result.code = 400
result.msg = msg
return result
@classmethod
def error(cls, msg: str):
"""系统异常响应"""
result = cls()
result.code = 500
result.msg = msg
return result
def to_dict(self):
"""转为字典格式"""
return {
'code': self.code,
'msg': self.msg,
'data': self.data
}
# 使用示例
if __name__ == "__main__":
# 成功响应
success_result = ResultVO.success("操作成功", {"id": 1, "name": "test"})
print(success_result.to_dict())
# 参数错误响应
param_error_result = ResultVO.param_error("参数校验失败")
print(param_error_result.to_dict())
# 系统错误响应
error_result = ResultVO.error("系统异常")
print(error_result.to_dict())```
**全局异常处理器**
```python
from flask import Flask, request, jsonify
from werkzeug.exceptions import HTTPException
import logging
app = Flask(__name__)
class ValidationError(Exception):
"""参数校验异常"""
def __init__(self, message):
self.message = message
super().__init__(message)
class ResultVO:
"""API结果封装类"""
@staticmethod
def param_error(msg: str):
return {
'code': 400,
'msg': msg,
'data': None
}
@staticmethod
def error(msg: str):
return {
'code': 500,
'msg': msg,
'data': None
}
@staticmethod
def success(msg: str, data=None):
return {
'code': 200,
'msg': msg,
'data': data
}
@app.errorhandler(ValidationError)
def handle_validation_error(error):
"""处理参数校验异常"""
return jsonify(ResultVO.param_error(error.message)), 400
@app.errorhandler(HTTPException)
def handle_http_exception(error):
"""处理HTTP异常"""
return jsonify(ResultVO.param_error(f"请求错误: {error.description}")), error.code
@app.errorhandler(Exception)
def handle_all_exceptions(error):
"""全局异常处理器"""
# 记录错误日志
logging.error(f"系统错误: {str(error)}", exc_info=True)
return jsonify(ResultVO.error("系统繁忙,请稍后重试")), 500
# 示例路由用于测试
@app.route('/test-validation', methods=['POST'])
def test_validation():
data = request.get_json() or {}
# 模拟参数校验
if not data.get('name'):
raise ValidationError("姓名不能为空")
if not isinstance(data.get('age'), int) or data['age'] < 0:
raise ValidationError("年龄必须为非负整数")
return jsonify(ResultVO.success("验证通过"))
if __name__ == '__main__':
app.run(debug=True)```
# 总结
这套API参数校验方案是SpringBoot项目的标准化通用方案,核心由请求体校验、查询参数校验、统一异常返回三部分组成,覆盖绝大多数接口开发场景。针对新增、修改类POST/PUT接口,采用@Valid注解实现复杂嵌套参数的全自动校验,从源头避免参数漏验、非法参数穿透业务层的问题;针对GET查询、分页接口,通过类注解@Validated快速完成简单参数的合法性校验,有效防止越界查询、非法字段查询导致的数据库性能问题。
配合全局异常处理器,统一拦截所有参数校验异常,规整接口返回格式,隐藏底层异常堆栈,既保障了系统安全性,又提升了前后端联调效率。整体方案摒弃了传统手动参数判断的繁琐写法,实现了参数校验前置、异常统一兜底、返回格式标准化的开发闭环,代码简洁、维护性强,完全适配生产环境落地使用