Spring Boot Controller 参数规范

目标:统一接口风格、减少歧义、避免隐式行为、提升可维护性与可读性


一、总体原则(必须遵守)

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 的行为是:

  1. 创建 UserDTO
  2. Query 参数 / Form 表单
  3. 按字段名匹配
  4. 调用 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 == null
  • user.age == null

📌 原因

  • 默认是 @ModelAttribute
  • JSON 只会被 @RequestBody 解析

✔ 正确写法:

public void save(@RequestBody UserDTO user)

❌ 误区 2:GET 和 POST 默认行为不同?

👉 不是

  • GET / POST 默认规则一致
  • 差别在于 数据放在哪

四、为什么 Spring 这么设计?(底层逻辑)

Spring 参数解析顺序(简化版):

  1. 是否有明确注解?
    • 有 → 按注解处理
  2. 是否是 Servlet 原生对象?
    • 是 → 直接注入
  3. 是否是简单类型?
    • 是 → @RequestParam
  4. 是否是复杂对象?
    • 是 → @ModelAttribute
  5. 是否是 @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)

正文完
 0
评论(没有评论)
验证码