RESTful 设计原则 #

概述 #

RESTful API 设计遵循一组核心原则,这些原则确保 API 具有一致性、可理解性和可维护性。本章将详细介绍这些设计原则。

text
┌─────────────────────────────────────────────────────────────┐
│                  RESTful 设计原则                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  资源导向    │  │  统一接口    │  │  无状态      │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  名词命名    │  │  层级清晰    │  │  版本控制    │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘

原则一:资源导向设计 #

核心思想 #

RESTful API 的核心是资源,而不是动作。所有的设计都围绕资源展开。

text
┌─────────────────────────────────────────────────────────────┐
│                    资源导向设计                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  错误示例(动作导向):                                       │
│                                                             │
│  GET  /getUsers                                             │
│  POST /createUser                                           │
│  POST /deleteUser?id=123                                    │
│  POST /updateUser?id=123                                    │
│                                                             │
│  正确示例(资源导向):                                       │
│                                                             │
│  GET    /users           获取用户列表                       │
│  POST   /users           创建用户                           │
│  DELETE /users/123       删除用户                           │
│  PUT    /users/123       更新用户                           │
│                                                             │
│  关键区别:                                                  │
│  - URL 只包含名词(资源)                                   │
│  - 动作由 HTTP 方法表达                                     │
│  - URL 表示资源,方法表示操作                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

资源识别 #

text
┌─────────────────────────────────────────────────────────────┐
│                    资源识别方法                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  识别资源的步骤:                                            │
│                                                             │
│  1. 分析业务领域                                            │
│     - 系统中有哪些实体?                                    │
│     - 实体之间的关系是什么?                                │
│     - 哪些需要暴露为 API?                                  │
│                                                             │
│  2. 确定资源类型                                            │
│     - 集合资源:/users                                      │
│     - 单个资源:/users/123                                  │
│     - 子资源:/users/123/orders                             │
│                                                             │
│  3. 定义资源属性                                            │
│     - 哪些属性需要暴露?                                    │
│     - 哪些属性只读?                                        │
│     - 哪些属性可修改?                                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

原则二:统一接口 #

HTTP 方法语义 #

text
┌─────────────────────────────────────────────────────────────┐
│                    HTTP 方法语义                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  方法        操作        幂等性    安全性    说明            │
│  ─────────────────────────────────────────────────────────  │
│  GET         查询        ✅        ✅       获取资源         │
│  POST        创建        ❌        ❌       新建资源         │
│  PUT         更新        ✅        ❌       完整更新         │
│  PATCH       更新        ❌        ❌       部分更新         │
│  DELETE      删除        ✅        ❌       删除资源         │
│  HEAD        查询头      ✅        ✅       获取元数据       │
│  OPTIONS     查询选项    ✅        ✅       获取支持方法     │
│                                                             │
│  幂等性:多次执行结果相同                                    │
│  安全性:不会修改资源状态                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

方法使用示例 #

text
┌─────────────────────────────────────────────────────────────┐
│                    方法使用示例                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  GET /users                                                 │
│  ─────────────────────────────────────────────              │
│  功能:获取用户列表                                         │
│  请求体:无                                                 │
│  响应:200 OK + 用户列表                                    │
│                                                             │
│  GET /users/123                                             │
│  ─────────────────────────────────────────────              │
│  功能:获取指定用户                                         │
│  请求体:无                                                 │
│  响应:200 OK + 用户信息 或 404 Not Found                   │
│                                                             │
│  POST /users                                                │
│  ─────────────────────────────────────────────              │
│  功能:创建用户                                             │
│  请求体:用户数据                                           │
│  响应:201 Created + Location: /users/124                   │
│                                                             │
│  PUT /users/123                                             │
│  ─────────────────────────────────────────────              │
│  功能:完整更新用户                                         │
│  请求体:完整用户数据                                       │
│  响应:200 OK 或 204 No Content                             │
│                                                             │
│  PATCH /users/123                                           │
│  ─────────────────────────────────────────────              │
│  功能:部分更新用户                                         │
│  请求体:部分用户数据                                       │
│  响应:200 OK 或 204 No Content                             │
│                                                             │
│  DELETE /users/123                                          │
│  ─────────────────────────────────────────────              │
│  功能:删除用户                                             │
│  请求体:无                                                 │
│  响应:204 No Content 或 200 OK                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

原则三:资源命名规范 #

命名规则 #

text
┌─────────────────────────────────────────────────────────────┐
│                    资源命名规则                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 使用名词,不使用动词                                     │
│     ✅ /users                                               │
│     ❌ /getUsers                                            │
│                                                             │
│  2. 使用复数形式                                             │
│     ✅ /users                                               │
│     ❌ /user                                                │
│                                                             │
│  3. 使用小写字母                                             │
│     ✅ /user-profiles                                       │
│     ❌ /UserProfiles                                        │
│                                                             │
│  4. 使用连字符分隔单词                                       │
│     ✅ /order-items                                         │
│     ❌ /orderItems                                          │
│     ❌ /order_items                                         │
│                                                             │
│  5. 避免文件扩展名                                           │
│     ✅ /users/123                                           │
│     ❌ /users/123.json                                      │
│                                                             │
│  6. 使用层级表示关系                                         │
│     ✅ /users/123/orders                                    │
│     ❌ /users/123-orders                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

命名示例对比 #

text
┌─────────────────────────────────────────────────────────────┐
│                    命名示例对比                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  场景:用户管理                                              │
│                                                             │
│  ❌ 错误命名:                                               │
│  /getUser                                                   │
│  /user                                                      │
│  /Users                                                     │
│  /user_info                                                 │
│  /userInfo                                                  │
│                                                             │
│  ✅ 正确命名:                                               │
│  /users                    用户集合                         │
│  /users/123                单个用户                         │
│  /users/123/profile        用户资料                         │
│  /users/123/orders         用户订单                         │
│                                                             │
│  ─────────────────────────────────────────────              │
│                                                             │
│  场景:订单管理                                              │
│                                                             │
│  ❌ 错误命名:                                               │
│  /orderList                                                 │
│  /create-order                                              │
│  /orderItems                                                │
│                                                             │
│  ✅ 正确命名:                                               │
│  /orders                   订单集合                         │
│  /orders/456              单个订单                          │
│  /orders/456/items        订单项                            │
│  /orders/456/items/1      单个订单项                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

原则四:状态码正确使用 #

状态码分类 #

text
┌─────────────────────────────────────────────────────────────┐
│                    HTTP 状态码分类                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1xx - 信息响应                                              │
│      请求已接收,继续处理                                    │
│                                                             │
│  2xx - 成功响应                                              │
│      请求已成功接收、理解、接受                              │
│                                                             │
│  3xx - 重定向                                                │
│      需要进一步操作以完成请求                                │
│                                                             │
│  4xx - 客户端错误                                            │
│      请求包含语法错误或无法完成                              │
│                                                             │
│  5xx - 服务端错误                                            │
│      服务器无法完成有效请求                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

常用状态码 #

text
┌─────────────────────────────────────────────────────────────┐
│                    常用状态码速查                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  成功响应(2xx):                                           │
│  ┌─────────┬─────────────────────────────────────┐          │
│  │ 200 OK  │ 请求成功,返回数据                   │          │
│  ├─────────┼─────────────────────────────────────┤          │
│  │ 201     │ 创建成功,返回 Location 头          │          │
│  │ Created │                                     │          │
│  ├─────────┼─────────────────────────────────────┤          │
│  │ 204     │ 成功但无返回内容(DELETE/PUT)       │          │
│  │ No Cont │                                     │          │
│  └─────────┴─────────────────────────────────────┘          │
│                                                             │
│  客户端错误(4xx):                                         │
│  ┌─────────┬─────────────────────────────────────┐          │
│  │ 400 Bad │ 请求格式错误或参数无效              │          │
│  │ Request │                                     │          │
│  ├─────────┼─────────────────────────────────────┤          │
│  │ 401     │ 未认证,需要登录                    │          │
│  │ Unauth  │                                     │          │
│  ├─────────┼─────────────────────────────────────┤          │
│  │ 403     │ 已认证但无权限                      │          │
│  │ Forbid  │                                     │          │
│  ├─────────┼─────────────────────────────────────┤          │
│  │ 404 Not │ 资源不存在                          │          │
│  │ Found   │                                     │          │
│  ├─────────┼─────────────────────────────────────┤          │
│  │ 409     │ 请求冲突(如重复创建)              │          │
│  │ Conflic │                                     │          │
│  ├─────────┼─────────────────────────────────────┤          │
│  │ 422     │ 语义错误,无法处理                  │          │
│  │ Unproce │                                     │          │
│  └─────────┴─────────────────────────────────────┘          │
│                                                             │
│  服务端错误(5xx):                                         │
│  ┌─────────┬─────────────────────────────────────┐          │
│  │ 500     │ 服务器内部错误                      │          │
│  │ Internal│                                     │          │
│  ├─────────┼─────────────────────────────────────┤          │
│  │ 502 Bad │ 网关错误                            │          │
│  │ Gateway │                                     │          │
│  ├─────────┼─────────────────────────────────────┤          │
│  │ 503     │ 服务不可用                          │          │
│  │ Service │                                     │          │
│  └─────────┴─────────────────────────────────────┘          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

原则五:无状态设计 #

无状态原则 #

text
┌─────────────────────────────────────────────────────────────┐
│                    无状态设计原则                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  定义:                                                      │
│  每个请求必须包含服务端处理所需的所有信息                    │
│  服务端不保存客户端会话状态                                  │
│                                                             │
│  实现方式:                                                  │
│                                                             │
│  1. 使用 Token 认证                                         │
│     Authorization: Bearer eyJhbGciOiJIUzI1NiIs...           │
│                                                             │
│  2. 请求包含必要参数                                        │
│     GET /users?page=2&limit=10                              │
│                                                             │
│  3. 不依赖服务端 Session                                    │
│     ❌ $_SESSION['user_id']                                 │
│     ✅ JWT Token 解析用户信息                               │
│                                                             │
│  优势:                                                      │
│  ✅ 水平扩展简单                                            │
│  ✅ 服务器重启不影响用户                                    │
│  ✅ 便于负载均衡                                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

有状态 vs 无状态 #

text
┌─────────────────────────────────────────────────────────────┐
│                    有状态 vs 无状态                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  有状态设计:                                                │
│                                                             │
│  请求1: POST /login                                         │
│  服务端: 创建 Session,存储用户信息                          │
│                                                             │
│  请求2: GET /profile                                        │
│  服务端: 查找 Session,获取用户信息                          │
│                                                             │
│  问题:                                                      │
│  ❌ 服务器需要存储 Session                                  │
│  ❌ 分布式环境需要 Session 共享                             │
│  ❌ 服务器重启 Session 丢失                                 │
│                                                             │
│  ─────────────────────────────────────────────              │
│                                                             │
│  无状态设计:                                                │
│                                                             │
│  请求1: POST /login                                         │
│  服务端: 验证成功,返回 Token                                │
│                                                             │
│  请求2: GET /profile                                        │
│  Header: Authorization: Bearer <token>                      │
│  服务端: 验证 Token,解析用户信息                            │
│                                                             │
│  优势:                                                      │
│  ✅ 服务端无需存储会话                                      │
│  ✅ 任何服务器都能处理请求                                  │
│  ✅ 天然支持分布式                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

原则六:内容协商 #

Accept 头使用 #

text
┌─────────────────────────────────────────────────────────────┐
│                    内容协商                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  客户端通过 Accept 头指定响应格式:                          │
│                                                             │
│  请求 JSON 格式:                                            │
│  GET /users/123                                             │
│  Accept: application/json                                   │
│                                                             │
│  响应:                                                      │
│  HTTP/1.1 200 OK                                            │
│  Content-Type: application/json                             │
│                                                             │
│  {                                                          │
│    "id": 123,                                               │
│    "name": "张三"                                           │
│  }                                                          │
│                                                             │
│  ─────────────────────────────────────────────              │
│                                                             │
│  请求 XML 格式:                                             │
│  GET /users/123                                             │
│  Accept: application/xml                                    │
│                                                             │
│  响应:                                                      │
│  HTTP/1.1 200 OK                                            │
│  Content-Type: application/xml                              │
│                                                             │
│  <?xml version="1.0"?>                                      │
│  <user>                                                     │
│    <id>123</id>                                             │
│    <name>张三</name>                                        │
│  </user>                                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

原则七:HATEOAS #

超媒体驱动 #

text
┌─────────────────────────────────────────────────────────────┐
│                    HATEOAS 原则                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  HATEOAS(Hypermedia as the Engine of Application State)   │
│  超媒体作为应用状态引擎                                      │
│                                                             │
│  原则:                                                      │
│  响应中包含相关链接,引导客户端进行下一步操作                │
│                                                             │
│  示例响应:                                                  │
│  {                                                          │
│    "id": 123,                                               │
│    "name": "张三",                                          │
│    "email": "zhangsan@example.com",                         │
│    "_links": {                                              │
│      "self": {                                              │
│        "href": "/users/123"                                 │
│      },                                                     │
│      "orders": {                                            │
│        "href": "/users/123/orders"                          │
│      },                                                     │
│      "update": {                                            │
│        "href": "/users/123",                                │
│        "method": "PUT"                                      │
│      },                                                     │
│      "delete": {                                            │
│        "href": "/users/123",                                │
│        "method": "DELETE"                                   │
│      }                                                      │
│    }                                                        │
│  }                                                          │
│                                                             │
│  优势:                                                      │
│  ✅ 客户端无需硬编码 URL                                    │
│  ✅ API 可动态演进                                          │
│  ✅ 自描述能力                                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

原则八:版本控制 #

版本策略 #

text
┌─────────────────────────────────────────────────────────────┐
│                    版本控制策略                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. URL 路径版本(推荐)                                     │
│     /v1/users                                               │
│     /v2/users                                               │
│                                                             │
│     优点:简单直观,易于缓存                                 │
│     缺点:URL 变化                                          │
│                                                             │
│  2. 查询参数版本                                             │
│     /users?version=1                                        │
│     /users?version=2                                        │
│                                                             │
│     优点:URL 不变                                          │
│     缺点:可选参数容易被忽略                                 │
│                                                             │
│  3. Header 版本                                              │
│     Accept: application/vnd.api.v1+json                     │
│     Accept: application/vnd.api.v2+json                     │
│                                                             │
│     优点:URL 不变,RESTful                                 │
│     缺点:不够直观                                          │
│                                                             │
│  推荐:使用 URL 路径版本,简单明了                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

原则九:过滤、排序、分页 #

查询参数设计 #

text
┌─────────────────────────────────────────────────────────────┐
│                    查询参数设计                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  过滤(Filtering):                                         │
│  GET /users?status=active                                   │
│  GET /users?role=admin                                      │
│  GET /products?category=electronics&price_min=100           │
│                                                             │
│  排序(Sorting):                                           │
│  GET /users?sort=created_at         升序                    │
│  GET /users?sort=-created_at        降序                    │
│  GET /users?sort=name,-created_at   多字段排序              │
│                                                             │
│  分页(Pagination):                                        │
│  GET /users?page=1&limit=20         页码分页                │
│  GET /users?offset=0&limit=20       偏移分页                │
│  GET /users?cursor=abc123&limit=20  游标分页                │
│                                                             │
│  字段选择(Field Selection):                               │
│  GET /users?fields=id,name,email                            │
│                                                             │
│  搜索(Search):                                            │
│  GET /users?q=zhang                                         │
│  GET /products?search=iPhone                                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

原则十:安全性 #

安全设计原则 #

text
┌─────────────────────────────────────────────────────────────┐
│                    安全设计原则                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 始终使用 HTTPS                                          │
│     ✅ https://api.example.com/users                        │
│     ❌ http://api.example.com/users                         │
│                                                             │
│  2. 认证与授权                                              │
│     - 使用 Token 认证(JWT)                                │
│     - 实现 RBAC 权限控制                                    │
│     - 验证用户权限                                          │
│                                                             │
│  3. 输入验证                                                │
│     - 验证所有输入参数                                      │
│     - 防止 SQL 注入                                         │
│     - 防止 XSS 攻击                                         │
│                                                             │
│  4. 速率限制                                                │
│     X-RateLimit-Limit: 100                                  │
│     X-RateLimit-Remaining: 95                               │
│     X-RateLimit-Reset: 1640995200                           │
│                                                             │
│  5. 敏感数据保护                                            │
│     - 不返回敏感字段(密码、密钥)                          │
│     - 日志脱敏                                              │
│     - 错误信息不暴露实现细节                                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

设计原则总结 #

text
┌─────────────────────────────────────────────────────────────┐
│                    设计原则速查表                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  原则              要点                     示例             │
│  ─────────────────────────────────────────────────────────  │
│  资源导向          URL 使用名词              /users          │
│  统一接口          HTTP 方法语义化           GET /users      │
│  命名规范          复数、小写、连字符        /order-items    │
│  状态码            正确使用 HTTP 状态码      201 Created     │
│  无状态            不依赖服务端会话          Token 认证      │
│  内容协商          Accept 头指定格式         application/json│
│  HATEOAS           响应包含相关链接          _links          │
│  版本控制          URL 路径版本              /v1/users       │
│  查询参数          过滤、排序、分页          ?page=1&limit=20│
│  安全性            HTTPS、认证、验证         Bearer Token    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

设计检查清单 #

text
□ URL 设计
  □ 使用名词而非动词
  □ 使用复数形式
  □ 使用小写字母
  □ 使用连字符分隔
  □ 层级结构清晰

□ HTTP 方法
  □ GET 用于查询
  □ POST 用于创建
  □ PUT 用于完整更新
  □ PATCH 用于部分更新
  □ DELETE 用于删除

□ 状态码
  □ 成功返回 2xx
  □ 客户端错误返回 4xx
  □ 服务端错误返回 5xx
  □ 创建成功返回 201
  □ 删除成功返回 204

□ 安全性
  □ 使用 HTTPS
  □ 实现认证机制
  □ 实现权限控制
  □ 输入验证
  □ 速率限制

□ 其他
  □ 版本控制
  □ 分页支持
  □ 错误处理
  □ 文档完善

下一步 #

现在你已经了解了 RESTful API 的设计原则,接下来学习 资源设计,深入了解如何设计合理的资源结构!

最后更新:2026-03-29