目标:统一接口风格、减少歧义、避免隐式行为、提升可维护性与可读性
一、总体原则(必须遵守)
1️⃣ Controller 参数 必须显式声明来源
❌ 禁止依赖 Spring 默认参数绑定行为
✅ 所有参数 必须使用明确注解
例外:
HttpServletRequest / Response
2️⃣ 一个接口只使用 一种主要参数来源
| 场景 | 允许 |
|---|---|
| 查询接口 | @RequestParam |
| 新增 / 修改 | @RequestBody |
| 资源定位 | @PathVariable |
❌ 禁止:
@RequestBody + 多个 @RequestParam 混用(非必要)
3️⃣ JSON 请求 必须使用 @RequestBody
❌ 不允许通过“裸 DTO 参数”接收 JSON
二、参数使用规范(强制)
1️⃣ 查询类接口(GET)
📌 规则
- 使用
@RequestParam - 多条件查询:封装为 QueryDTO
- 分页参数统一命名
@GetMapping("/orders")
public PageResult<OrderVO> list(
@RequestParam(required = false) String orderNo,
@RequestParam(required = false) Long userId,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
}
✅ 推荐(复杂查询):
@GetMapping("/orders")
public PageResult<OrderVO> list(@ModelAttribute OrderQuery query) {
}
2️⃣ 新增接口(POST)
📌 规则
- 使用
@RequestBody - 使用 DTO,不直接使用 Entity
- 必须配合参数校验
@PostMapping("/orders")
public void create(@RequestBody @Valid OrderCreateDTO dto) {
}
❌ 禁止:
public void create(Order entity)
3️⃣ 修改接口(PUT / POST)
@PutMapping("/orders/{id}")
public void update(
@PathVariable Long id,
@RequestBody @Valid OrderUpdateDTO dto) {
}
📌 ID 必须从路径获取
4️⃣ 删除接口(DELETE)
@DeleteMapping("/orders/{id}")
public void delete(@PathVariable Long id) {
}
❌ 禁止:
@RequestParam id- Body 传 ID
三、路径参数规范(RESTful)
| 场景 | 写法 |
|---|---|
| 单个资源 | /orders/{id} |
| 子资源 | /users/{userId}/orders |
| 行为操作 | /orders/{id}/cancel |
四、Header / Token 规范
1️⃣ Token 获取方式
- 统一使用 Header
- 不允许 Query 传 Token
@RequestHeader("Authorization") String token
五、文件上传规范
1️⃣ 单文件上传
@PostMapping("/upload")
public void upload(@RequestParam MultipartFile file) {
}
2️⃣ 文件 + JSON 混合
@PostMapping("/upload")
public void upload(
@RequestPart("file") MultipartFile file,
@RequestPart("meta") @Valid FileMetaDTO meta) {
}
六、参数校验规范(强制)
1️⃣ Controller 层开启校验
- 使用
@Valid/@Validated
@PostMapping("/user")
public void save(@RequestBody @Valid UserDTO dto) {
}
2️⃣ DTO 内使用校验注解
@NotNull(message = "用户ID不能为空")
private Long userId;
七、禁止事项(重点)
❌ Controller 使用 Entity 作为参数
❌ 不写注解依赖默认行为
❌ GET 使用 @RequestBody
❌ JSON 和 Query 参数混乱
❌ Controller 写业务逻辑
八、推荐接口参数模板(统一)
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@GetMapping
public PageResult<OrderVO> list(@ModelAttribute OrderQuery query) {
}
@PostMapping
public void create(@RequestBody @Valid OrderCreateDTO dto) {
}
@PutMapping("/{id}")
public void update(
@PathVariable Long id,
@RequestBody @Valid OrderUpdateDTO dto) {
}
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
}
}
九、为什么要这么做?
- 避免隐式行为导致 Bug
- 便于接口文档生成(Knife4j / Swagger)
- 提高代码可读性
- 降低新人学习成本
- 支撑长期维护与重构
结论:
Controller 层 = 参数接收 + 转发
不负责业务,不依赖“默认”
这个问题问得非常关键 👍
很多 Spring Boot “看起来能跑”的代码,核心就在这里。
一句话结论(先记住)
Controller 方法参数如果什么注解都不加:
👉 默认按@ModelAttribute方式处理(表单 / URL 参数绑定)
❗不是 @RequestBody,也不是“自动 JSON”
一、什么都不加时,Spring 到底做了什么?
来看一个最常见的例子:
@GetMapping("/user")
public void user(UserDTO user) {
}
请求:
GET /user?name=tom&age=18
Spring 的行为是:
- 创建
UserDTO - 从 Query 参数 / Form 表单
- 按字段名匹配
- 调用
setter
📌 等价于:
public void user(@ModelAttribute UserDTO user) {
}
二、不同参数类型的「默认行为」对照表 ⭐
1️⃣ 普通类型(String / int / Long)
@GetMapping("/test")
public void test(String name, Integer age) {
}
请求:
/test?name=tom&age=18
📌 默认等价于:
public void test(
@RequestParam String name,
@RequestParam Integer age) {
}
❗注意:
- 默认 required = true
- 参数不存在会 400
2️⃣ JavaBean / DTO 对象(最常见)
public void test(UserDTO user)
📌 默认:
@ModelAttribute UserDTO user
支持的数据来源:
- Query 参数
- Form 表单(x-www-form-urlencoded)
- multipart 表单
❌ 不支持 JSON
3️⃣ Map / MultiValueMap
public void test(Map<String, String> map) {
}
📌 默认:
- 接收所有 Query/Form 参数
- key = 参数名
4️⃣ HttpServletRequest / Response
public void test(HttpServletRequest request) {
}
📌 直接注入(Servlet 原生对象)
✔ 无需任何注解
三、重点误区(90% 新手踩过)
❌ 误区 1:不写注解也能接 JSON?
@PostMapping("/user")
public void save(UserDTO user) {
}
请求:
POST /user
Content-Type: application/json
{ "name": "tom", "age": 18 }
🚨 结果:
user.name == nulluser.age == null
📌 原因
- 默认是
@ModelAttribute - JSON 只会被
@RequestBody解析
✔ 正确写法:
public void save(@RequestBody UserDTO user)
❌ 误区 2:GET 和 POST 默认行为不同?
👉 不是
- GET / POST 默认规则一致
- 差别在于 数据放在哪
四、为什么 Spring 这么设计?(底层逻辑)
Spring 参数解析顺序(简化版):
- 是否有明确注解?
- 有 → 按注解处理
- 是否是 Servlet 原生对象?
- 是 → 直接注入
- 是否是简单类型?
- 是 →
@RequestParam
- 是 →
- 是否是复杂对象?
- 是 →
@ModelAttribute
- 是 →
- 是否是
@RequestBody?- 只有你明确声明才走
📌 Spring 永远不会“猜 JSON”
五、一张“默认规则速查表” ⭐(建议记)
| 参数类型 | 不加注解时 |
|---|---|
| String / int / Long | @RequestParam |
| JavaBean / DTO | @ModelAttribute |
| List / Map | 参数集合 |
| MultipartFile | 表单文件 |
| HttpServletRequest | 直接注入 |
| JSON Body | ❌ 接不到 |
六、真实项目建议(经验)
✅ 建议你在团队里:
- DTO + JSON:必须写
@RequestBody - 简单参数:
@RequestParam - ID:
@PathVariable - 禁止“靠默认”
📌 原因:
- 可读性
- 避坑
- 自动生成 API 文档(Knife4j / Swagger)
正文完
