返回博客
技术 2025年2月11日 4 分钟阅读 · 966 字

RESTful API 设计最佳实践:从 URI 规范到错误处理的完整指南

构建可维护、可扩展的 RESTful API 需要遵循的 10 条核心原则,涵盖 URI 设计、状态码、分页、版本控制与安全。

#RESTful API #后端开发 #HTTP #API 设计
本文由 AI 辅助生成,经人工审核发布

1. URI 设计:名词而非动词

REST 的核心是资源,URI 应该用名词描述资源,HTTP 方法描述操作:

# ✅ 正确
GET    /api/users          # 获取用户列表
POST   /api/users          # 创建用户
GET    /api/users/123      # 获取单个用户
PUT    /api/users/123      # 更新用户(完整替换)
PATCH  /api/users/123      # 部分更新
DELETE /api/users/123      # 删除用户

# ❌ 错误
GET    /api/getUsers
POST   /api/createUser
GET    /api/deleteUserById?id=123

嵌套资源

GET /api/users/123/orders          # 用户的订单列表
GET /api/users/123/orders/456      # 用户的特定订单

嵌套层级建议不超过 2 层。更深的关联用查询参数:

GET /api/orders?user_id=123&status=paid

2. HTTP 状态码

使用语义化的状态码,不要所有响应都返回 200:

状态码含义使用场景
200 OK成功GET、PUT、PATCH 成功
201 Created已创建POST 成功创建资源
204 No Content无内容DELETE 成功
400 Bad Request请求错误参数验证失败
401 Unauthorized未认证缺少/无效的 Token
403 Forbidden禁止权限不足
404 Not Found不存在资源未找到
409 Conflict冲突唯一约束冲突
422 Unprocessable无法处理业务逻辑验证失败
429 Too Many Requests频率限制限流触发

统一错误响应格式

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "请求参数验证失败",
    "details": [
      { "field": "email", "message": "邮箱格式不正确" },
      { "field": "password", "message": "密码至少 8 位" }
    ]
  }
}

3. 分页、排序与过滤

分页

GET /api/users?page=2&per_page=20

响应中包含分页元数据:

{
  "data": [...],
  "meta": {
    "current_page": 2,
    "per_page": 20,
    "total": 150,
    "last_page": 8
  },
  "links": {
    "prev": "/api/users?page=1&per_page=20",
    "next": "/api/users?page=3&per_page=20"
  }
}

游标分页(大数据量)

传统分页在数据量大时性能差(OFFSET 100000 需要扫描 100000 行)。游标分页用上一页最后一条记录的 ID 作为起点:

GET /api/users?cursor=eyJpZCI6MTAwfQ&per_page=20
-- 传统分页:慢
SELECT * FROM users ORDER BY id DESC LIMIT 20 OFFSET 100000;

-- 游标分页:快
SELECT * FROM users WHERE id < 100 ORDER BY id DESC LIMIT 20;

排序

GET /api/users?sort=-created_at,name
# - 表示降序,多个字段用逗号分隔

过滤

GET /api/orders?status=paid&amount_min=100&amount_max=500

4. 版本控制

URI 版本(推荐)

GET /api/v1/users
GET /api/v2/users

简单直接,易于调试和缓存。

Header 版本

GET /api/users
Accept: application/vnd.myapp.v2+json

不污染 URI,但调试困难,需要文档说明。

5. 请求与响应格式

请求 Content-Type

POST /api/users
Content-Type: application/json

{ "name": "Li Xiang", "email": "li@example.com" }

字段命名

统一使用 snake_casecamelCase,团队内保持一致:

{
  "user_id": 123,
  "created_at": "2025-01-15T10:30:00Z",
  "is_active": true
}

时间格式

始终使用 ISO 8601 UTC:

{ "created_at": "2025-01-15T10:30:00Z" }

不要使用时间戳(1736933400)或本地时间格式。

6. 认证与授权

Bearer Token

GET /api/users/123
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

API Key(服务间调用)

GET /api/internal/sync
X-API-Key: your-secret-key

7. 限流

在响应头中返回限流信息:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1736933700

超过限制时返回 429:

{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "请求过于频繁,请稍后再试",
    "retry_after": 60
  }
}

8. 幂等性

方法幂等安全
GET
POST
PUT
DELETE
PATCH

DELETE /api/users/123 调用一次和调用十次效果相同——用户被删除。客户端可以在网络超时后安全重试。

9. CORS 配置

// Laravel CORS 配置
'paths' => ['api/*'],
'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
'allowed_origins' => ['https://example.com'],
'allowed_headers' => ['Content-Type', 'Authorization'],

生产环境不要使用 * 作为 allowed_origins

10. 文档与测试

OpenAPI / Swagger

用注释生成 API 文档:

/**
 * @OA\Get(
 *   path="/api/users/{id}",
 *   @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")),
 *   @OA\Response(response=200, description="用户详情",
 *     @OA\JsonContent(ref="#/components/schemas/User")
 *   ),
 *   @OA\Response(response=404, description="用户不存在")
 * )
 */

集成测试

public function test_can_create_user(): void
{
    $response = $this->postJson('/api/v1/users', [
        'name' => 'Li Xiang',
        'email' => 'li@example.com',
        'password' => 'secure-password',
    ]);

    $response->assertStatus(201)
        ->assertJsonStructure([
            'data' => ['id', 'name', 'email', 'created_at']
        ]);
}

总结

好的 RESTful API 设计核心是一致性可预测性。开发者看到 GET /api/orders/123/items 就应该知道这是获取订单 123 的商品列表,不需要查文档。遵循这些规范可以显著降低团队协作和后续维护的成本。