接口路径规范

4 阅读6分钟

彻底搞懂 @GetMapping@PostMapping@PutMapping@DeleteMapping 等注解中路径参数的含义,以及它们和类上 @RequestMapping 的配合方式。

✅ 一、Mapping 注解和访问路径的关系

在 Spring Boot 的控制器中:

  • 类上的 @RequestMapping 定义了该类中所有接口的 公共前缀
  • 方法上的 @GetMapping@PostMapping 定义了该方法的 子路径
  • 最终接口访问地址 = 类上路径 + 方法上路径。

✅ 你的示例

@RestController
@RequestMapping("/admin/order")  // 类级别公共前缀
public class OrderController {

    @GetMapping("/add")          // 方法级别路径
    public String addOrder() {
        return "add order";
    }
}

🔹 这里最终的访问路径是:

/admin/order/add

你的理解是完全正确的:类上的 /admin/order 和方法上的 /add 组成完整路径。


✅ 二、Mapping 注解参数详解

@GetMapping 为例(PostMappingPutMappingDeleteMapping 都类似),它有几个常用参数:

1️⃣ value(或 path

  • 表示当前方法相对于类上 @RequestMapping 的子路径。
  • 可以是字符串或字符串数组。
  • valuepath 是同义别名,任选其一即可。

例:

@GetMapping("/list")
@GetMapping(path="/list")

以上两种写法完全等价。


2️⃣ params

  • 指定 必须带上哪些请求参数 才能匹配此方法。
  • 常用于区分同一路径下根据参数不同选择不同方法。

例:

@GetMapping(value="/search", params="type=quick")
public String quickSearch() { ... }

@GetMapping(value="/search", params="type=advanced")
public String advancedSearch() { ... }

🔹 /search?type=quick 会匹配到 quickSearch
🔹 /search?type=advanced 会匹配到 advancedSearch


同一路径根据请求参数不同来走不同处理逻辑

✅ 背景

在正常情况下,如果你有两个方法,它们的路径和请求方法都一样,比如都是:

GET /search

那么 Spring 会认为这是“路由冲突”:它不知道到底该走哪个方法,会抛出类似

Ambiguous mapping. Cannot map 'xxxController' method

的异常。


✅ 但是通过 params 区分

你可以在 @GetMappingparams 参数中 指定请求必须携带哪些参数才能匹配到这个方法,从而在同一路径下用不同的参数值区分不同方法。


✅ 你的例子

@RestController
@RequestMapping("/api")
public class SearchController {

    @GetMapping(value="/search", params="type=quick")
    public String quickSearch() {
        return "Quick search result";
    }

    @GetMapping(value="/search", params="type=advanced")
    public String advancedSearch() {
        return "Advanced search result";
    }
}

🔹 这里有两种情况:

情况 1:/api/search?type=quick

  • 发送 GET 请求:

    GET /api/search?type=quick
    
  • Spring 会检查:

    • URL 匹配 /search
    • params 中有 type=quick
  • 所以只会匹配到:

    public String quickSearch()
    
  • 响应是 "Quick search result"。

情况 2:/api/search?type=advanced

  • 发送 GET 请求:

    GET /api/search?type=advanced
    
  • URL 依然是 /search,但参数变成了 type=advanced

  • 所以只会匹配到:

    public String advancedSearch()
    
  • 响应是 "Advanced search result"。


✅ 这个 params 是什么意思?

@GetMapping 中:

@GetMapping(value="/search", params="type=quick")

这里的 params="type=quick" 表示:

  • 这个方法只会匹配请求中带有参数 type,并且其值必须等于 quick 的请求。

✅ 使用场景

为什么要这样做?

🔹 如果你要做 同一路径,但不同搜索类型 的接口,比如搜索可以按:

  • type=quick → 快速搜索
  • type=advanced → 高级搜索

而你又想让它们都用 /search 这个统一 URL(不想搞 /quickSearch/advancedSearch 两个接口),这时就非常适合用 params


✅ 注意

  • 如果没有带指定参数,或者参数值不符合任何一个方法定义的条件,Spring 会返回 404。

  • 你可以在 params 中同时指定多个参数,比如:

    @GetMapping(value="/search", params={"type=quick", "sort=asc"})
    

    表示请求中必须同时满足 type=quicksort=asc


✅ 小结

✔️ 通过 params 你可以:

  • 在同一个路径下,用不同的参数组合区分不同的接口处理方法;
  • 避免在 URL 路径上区分不同功能,让 API 更加 RESTful。

✅ 一句话总结:

params="type=quick" 表示只有当 GET 请求 /search 携带参数 type=quick 时,Spring 才会将请求路由到对应方法。

3️⃣ headers

  • 指定必须带上哪些请求头才能匹配此方法。
  • 用得不多,适合对 header 严格要求的接口。

例:

@GetMapping(value="/secure", headers="X-API-KEY=mykey")
public String secureApi() { ... }

4️⃣ produces

  • 指定返回的内容类型(响应类型)必须满足客户端的 Accept 头,否则不匹配。
  • 适合一个接口同时支持多种返回格式。

例:

@GetMapping(value="/data", produces="application/json")
public Data getJsonData() { ... }

@GetMapping(value="/data", produces="application/xml")
public Data getXmlData() { ... }

5️⃣ consumes

  • 指定接口能接收的请求体内容类型(Content-Type),常用于 POST/PUT。

例:

@PostMapping(value="/upload", consumes="application/json")
public String uploadJson(@RequestBody MyData data) { ... }

✅ 三、几个 Mapping 注解的区别

  • @RequestMapping

    • 通用注解,可通过 method= RequestMethod.GET/POST/... 指定请求方法。
    • 可以同时用于类或方法上。
    • 更灵活,但更繁琐。
  • @GetMapping

    • 相当于 @RequestMapping(method=RequestMethod.GET) 的简化版本。
    • 只处理 GET 请求,其他 @XxxMapping 类似。
  • @PostMapping

    • 简化处理 POST 请求。
  • @PutMapping

    • 简化处理 PUT 请求。
  • @DeleteMapping

    • 简化处理 DELETE 请求。

✅ 例子:全部组合演示

@RestController
@RequestMapping("/api/v1/users")  // 公共前缀:/api/v1/users
public class UserController {

    @GetMapping("/{id}")          // GET /api/v1/users/123
    public String getUser(@PathVariable Long id) { ... }

    @PostMapping                  // POST /api/v1/users
    public String createUser() { ... }

    @PutMapping("/{id}")          // PUT /api/v1/users/123
    public String updateUser(@PathVariable Long id) { ... }

    @DeleteMapping("/{id}")       // DELETE /api/v1/users/123
    public String deleteUser(@PathVariable Long id) { ... }
}

✅ 四、几点易错点

✅ 方法路径前不要随意加或漏掉斜杠:

  • /addadd 效果相同,但最好统一加 /

✅ 类上 @RequestMapping 的路径不要加尾部 /

  • 推荐 /admin/order 而不是 /admin/order/,避免和方法路径拼接时产生双斜杠。

✅ 不要把方法上路径写成绝对路径(以 / 开头和不以 / 开头都可以,但不要搞混):

  • /addadd 都能正常拼接,但统一风格更好。

🔹 在 REST 风格中, “动作”通过请求方法来区分,而不是靠路径来区分。


✅ 关键点:URL + HTTP 方法共同决定唯一接口

比如:

  • PUT /api/v1/users/123 → 表示对 id=123 的用户进行更新。
  • DELETE /api/v1/users/123 → 表示对 id=123 的用户进行删除。

虽然路径完全一致(/api/v1/users/123),但是:

  • 客户端 在发起请求时,指定了不同的 HTTP 方法(PUT 或 DELETE);
  • Spring Boot 会根据方法注解 @PutMapping / @DeleteMapping 中内置的请求方法去匹配不同的处理函数;
  • 所以对于同一路径,Spring 会根据请求的方法(Method)路由到正确的处理器。

✅ 在 Java Spring 中是怎么匹配的?

Spring 底层机制:

  • @PutMapping("/{id}") 本质等价于:

    @RequestMapping(value="/{id}", method=RequestMethod.PUT)
    
  • @DeleteMapping("/{id}") 等价于:

    @RequestMapping(value="/{id}", method=RequestMethod.DELETE)
    
  • Spring DispatcherServlet 根据:

    • 请求的 URL
    • 请求的 HTTP Method
      这两个条件匹配 Controller 方法。

✅ 调用示例

你通过不同方式发送不同方法的请求:

🔹 修改用户(PUT):

curl -X PUT http://localhost:8080/api/v1/users/123 \
     -H "Content-Type: application/json" \
     -d '{"name":"newname"}'

🔹 删除用户(DELETE):

curl -X DELETE http://localhost:8080/api/v1/users/123
  • 两次请求的 URL 完全一样;
  • 但第一次是 PUT 方法 → 匹配 @PutMapping
  • 第二次是 DELETE 方法 → 匹配 @DeleteMapping

✅ 这就是 RESTful API 的核心思想:

  • URL 表示 资源的位置

  • HTTP 方法表示 对资源的动作

    • GET → 查询
    • POST → 新增
    • PUT → 更新
    • PATCH → 部分更新
    • DELETE → 删除