Skip to content

04 · 路径 & 操作:paths / operations

生活类比paths 就是外卖菜单的菜品列表—— 路径 = 菜名("宫保鸡丁"), HTTP 方法 = 你跟服务员说的话("我点这个" / "把这个换成微辣" / "这个不要了"), 操作(operation)= 一道菜的完整描述(菜名 + 配料 + 做法 + 出餐时间)。


1. 一个 path 长什么样

yaml
paths:
  /users:               # ← 路径
    get:                # ← 方法(operation)
      summary: 用户列表
      tags: [user]
      parameters: [...]
      responses:
        '200': {...}
    post:               # ← 同一路径可有多个方法
      summary: 创建用户
      requestBody: {...}
      responses:
        '201': {...}

结构

paths
└── /xxx                 (路径)
     ├── get             (操作 1)
     ├── post            (操作 2)
     ├── put             (操作 3)
     ├── delete          (操作 4)
     └── parameters      (路径级参数,所有 method 共享)

2. HTTP 方法(七种)大全

方法语义幂等?安全?一般状态码类比
GET读取资源200, 404看菜单
POST创建资源 / 任意动作201, 200, 400下单
PUT完整更新(替换)200, 204退掉重新下整桌
PATCH局部更新(打补丁)❌*200, 204加一份米饭
DELETE删除资源204, 200退单
HEAD同 GET 但不返回 body200, 404问"今天有这道菜吗"
OPTIONS询问支持的方法 / CORS 预检204, 200问"你们能做啥"

幂等:同样请求执行 N 次效果跟 1 次一样。 安全:不会改服务端状态。 *PATCH 是否幂等取决于实现,比如 { "+balance": 100 } 不幂等,{ "name": "Alice" } 幂等。

YAML 写法

yaml
paths:
  /users:
    get:    {...}
    post:   {...}
  /users/{id}:
    get:    {...}
    put:    {...}
    patch:  {...}
    delete: {...}
    head:   {...}
    options:{...}

3. operation 对象的所有字段

每个方法(get/post/...)下面可以写:

yaml
get:
  tags: [user]                    # 分组
  summary: 获取用户列表             # 一句话简介(UI 折叠条上)
  description: |                  # 详细描述(支持 Markdown)
    返回当前用户列表,支持分页。
  operationId: listUsers          # 唯一 ID(代码生成时变成方法名)
  parameters: [...]               # 入参(query/path/header/cookie)
  requestBody: {...}              # 请求体(POST/PUT/PATCH 用)
  responses: {...}                # 响应(按状态码分组)
  security: []                    # 鉴权(覆盖全局)
  deprecated: false               # 是否废弃
  servers: [...]                  # 这个接口单独的服务器(少用)
  callbacks: {...}                # 异步回调(webhook 风格)
  externalDocs: {...}             # 外链

实战中最常用的是:summary + tags + parameters + requestBody + responses


4. summary vs description 怎么写

yaml
summary: 创建订单
description: |
  ## 业务规则
  - 商品库存不足时返回 409
  - 用户余额不足时返回 402

  ## 限流
  每用户每秒最多 2 次

  > 详见 [订单文档](https://wiki.example.com/order)

UI 显示效果:

┌─────────────────────────────────────────────────────┐
│ POST /orders   创建订单           ← summary          │
├─────────────────────────────────────────────────────┤
│ 业务规则                            ← description    │
│ • 商品库存不足时返回 409                             │
│ ...                                                 │
└─────────────────────────────────────────────────────┘

经验:summary 写动词短语("创建订单"),description 写业务规则


5. operationId 不是装饰品

yaml
paths:
  /users:
    get:
      operationId: listUsers       # ← 见名知意
    post:
      operationId: createUser

  /users/{id}:
    get:
      operationId: getUser
    put:
      operationId: updateUser
    delete:
      operationId: deleteUser

代码生成时(第 8 章)会变成:

typescript
// 生成的前端 SDK
export class UserApi {
  async listUsers(): Promise<User[]> { ... }
  async createUser(user: User): Promise<User> { ... }
  async getUser(id: number): Promise<User> { ... }
  async updateUser(id: number, user: User): Promise<User> { ... }
  async deleteUser(id: number): Promise<void> { ... }
}

强烈推荐每个 operation 都写 operationId,否则代码生成器会用 usersGetusersIdGet 这种丑名字。

命名约定:驼峰命名 + 动词在前

  • listUsersgetUserByIdupdateUserAvatar
  • users_getPOST_usersapi_create

6. 路径参数 {xxx} 的写法

OpenAPI 规定路径参数用 {} 包起来:

yaml
paths:
  /users/{id}:
    get:
      parameters:
        - name: id              # ← 必须跟 {} 里同名
          in: path              # ← 必须 path
          required: true        # ← 必须 true
          schema:
            type: integer
            minimum: 1
      responses:
        '200': {...}

  /users/{userId}/orders/{orderId}:    # 多个路径参数
    get:
      parameters:
        - name: userId
          in: path
          required: true
          schema: { type: integer }
        - name: orderId
          in: path
          required: true
          schema: { type: integer }
      responses:
        '200': {...}

第 5 章会专门讲所有参数类型。


7. 路径设计 RESTful 风格 · 经验法则

操作URL方法
列表/usersGET
创建/usersPOST
单条/users/{id}GET
完整更新/users/{id}PUT
局部更新/users/{id}PATCH
删除/users/{id}DELETE
子资源列表/users/{id}/ordersGET
子资源单条/users/{id}/orders/{orderId}GET
自定义动作(不能用 method 表达)/users/{id}:reset-passwordPOST

经验法则

  • 名词复数,不用动词:✅ /users/getUsers
  • 用 HTTP 方法表达动作,不用 URL:✅ DELETE /users/1GET /users/delete?id=1
  • 嵌套不超过 2 层:/users/1/orders/5 可以,/users/1/orders/5/items/3/refunds/2 就该拆
  • 实在不是 CRUD 的,加 :actionPOST /orders/{id}:cancelPOST /users/{id}:reset-password

8. 同路径多方法 · parameters 复用

某些参数(如 id)所有方法都要用,可以提到 path 级别:

yaml
paths:
  /users/{id}:
    parameters:                       # ← 路径级,所有方法共享
      - name: id
        in: path
        required: true
        schema: { type: integer }
    get:
      summary: 获取用户
      responses: {...}
    put:
      summary: 更新用户
      requestBody: {...}
      responses: {...}
    delete:
      summary: 删除用户
      responses: {...}

不用每个方法里都写一遍 id 参数。


9. deprecated · 废弃但暂不删除

yaml
paths:
  /v1/users:
    get:
      deprecated: true              # UI 会画删除线 + 警告
      summary: "[废弃] 请用 /v2/users"
      responses:
        '200': {...}

原则:废弃 ≠ 删除。先标 deprecated、推动下游迁移、等使用率到 0 再真删。


10. tags 多重分组

yaml
paths:
  /admin/users:
    get:
      tags: [admin, user]      # 同时归属两个分组
      summary: 管理员查看用户
      responses: {...}

UI 上这个接口会同时出现在 "admin" 和 "user" 两个折叠组下。


11. 实战:一个完整的"博客 API" paths

yaml
openapi: 3.0.3
info:
  title: 博客 API
  version: 1.0.0

tags:
  - name: post
  - name: comment
  - name: user

paths:
  /posts:
    get:
      tags: [post]
      operationId: listPosts
      summary: 获取文章列表
      parameters:
        - name: page
          in: query
          schema: { type: integer, default: 1 }
        - name: tag
          in: query
          schema: { type: string }
      responses:
        '200':
          description: 文章列表
    post:
      tags: [post]
      operationId: createPost
      summary: 发表文章
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title: { type: string }
                content: { type: string }
              required: [title, content]
      responses:
        '201':
          description: 创建成功

  /posts/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
    get:
      tags: [post]
      operationId: getPost
      responses:
        '200': { description: 文章详情 }
        '404': { description: 找不到 }
    put:
      tags: [post]
      operationId: updatePost
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title: { type: string }
                content: { type: string }
      responses:
        '200': { description: 更新成功 }
    delete:
      tags: [post]
      operationId: deletePost
      responses:
        '204': { description: 删除成功 }

  /posts/{id}/comments:
    get:
      tags: [comment]
      operationId: listComments
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        '200': { description: 评论列表 }
    post:
      tags: [comment]
      operationId: createComment
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                content: { type: string }
      responses:
        '201': { description: 评论成功 }

  /posts/{id}:like:
    post:
      tags: [post]
      operationId: likePost
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        '200': { description: 点赞成功 }

12. 章末面试题速览

  1. PUT 和 PATCH 的区别? → PUT 是替换整个资源(即使你只改一个字段,也要把所有字段传齐);PATCH 是只改你传的字段,其他保留。
  2. POST 和 PUT 都能创建资源,怎么选? → URL 里不知道 ID 时用 POST(服务端生成 ID);知道 ID 时用 PUT(客户端指定 ID,例:PUT /users/u-123)。
  3. operationId 写不写有差别吗? → 写:代码生成器生成的方法名好看(createUser());不写:随机生成(usersPost())。强烈推荐写。

🎬 可视化演示

下方 demo 是一个 "HTTP 方法选择器"——选不同方法 + URL,看典型响应、状态码、是否幂等等。

→ 打开 04_paths_operations/demo.html

🎬 可视化演示

演示加载缓慢或样式异常?点此在新标签页打开 ↗