API 设计与 RESTful 规范 良好的 API 设计是现代应用架构的核心,RESTful 风格是目前最流行的 API 设计规范。
RESTful 基础概念 REST 架构原则 客户端-服务器分离 :客户端和服务器端独立演进无状态 :每个请求包含所有必要信息可缓存 :响应应明确标明可缓存性统一接口 :标准的接口约定分层系统 :中间层不影响直接通信按需代码 :服务器可向客户端传输可执行代码资源与 URI 设计 # 好的 URI 设计 GET /api/v1/users # 获取用户列表 GET /api/v1/users/123 # 获取特定用户 POST /api/v1/users # 创建用户 PUT /api/v1/users/123 # 更新用户 DELETE /api/v1/users/123 # 删除用户 # 资源关系 GET /api/v1/users/123/orders # 获取用户的订单 GET /api/v1/orders/456/items # 获取订单的项目 POST /api/v1/users/123/orders # 为用户创建订单 # 避免的设计 GET /api/v1/getUsers # 不使用动词 GET /api/v1/users.php # 不使用文件扩展名 GET /api/v1/user_data # 使用复数形式
HTTP 方法使用 标准 HTTP 方法 方法 操作 幂等性 安全性 示例 GET 查询 ✓ ✓ GET /users POST 创建 ✗ ✗ POST /users PUT 完整更新 ✓ ✗ PUT /users/123 PATCH 部分更新 ✓ ✗ PATCH /users/123 DELETE 删除 ✓ ✗ DELETE /users/123 HEAD 获取头信息 ✓ ✓ HEAD /users OPTIONS 获取选项 ✓ ✓ OPTIONS /users
实际应用示例 # 创建资源 POST /api/v1/users Content-Type : application/json{ "name": "John Doe", "email": "john@example.com", "password": "securePassword" } # 响应 HTTP/1.1 201 CreatedLocation : /api/v1/users/123Content-Type : application/json{ "id" : 123, "name" : "John Doe" , "email" : "john@example.com" , "createdAt" : "2024-12-15T10:30:00Z" } PUT /api/v1/users/123 Content-Type: application/json { "name" : "John Smith" , "email" : "johnsmith@example.com" } PATCH /api/v1/users/123 Content-Type: application/json-patch+json [ { "op" : "replace" , "path" : "/name" , "value" : "John Smith" }, { "op" : "add" , "path" : "/phone" , "value" : "+1234567890" } ]
状态码规范 常用状态码 # 成功响应 200 OK # 请求成功 201 Created # 资源创建成功 204 No Content # 成功但无内容返回 # 客户端错误 400 Bad Request # 请求参数错误 401 Unauthorized # 未认证 403 Forbidden # 权限不足 404 Not Found # 资源不存在 409 Conflict # 资源冲突 422 Unprocessable Entity # 请求格式正确但语义错误 # 服务器错误 500 Internal Server Error # 服务器内部错误 502 Bad Gateway # 网关错误 503 Service Unavailable # 服务不可用
错误响应格式 { "error" : { "code" : "USER_NOT_FOUND" , "message" : "User with ID 123 not found" , "details" : { "userId" : 123 , "timestamp" : "2024-12-15T10:30:00Z" } } } { "error" : { "code" : "VALIDATION_ERROR" , "message" : "Request validation failed" , "details" : { "fields" : { "email" : "Invalid email format" , "password" : "Password must be at least 8 characters" } } } }
数据格式设计 请求响应格式 { "data" : [ { "id" : 1 , "name" : "John Doe" , "email" : "john@example.com" } , { "id" : 2 , "name" : "Jane Smith" , "email" : "jane@example.com" } ] , "pagination" : { "page" : 1 , "limit" : 20 , "total" : 100 , "totalPages" : 5 } , "links" : { "self" : "/api/v1/users?page=1&limit=20" , "next" : "/api/v1/users?page=2&limit=20" , "last" : "/api/v1/users?page=5&limit=20" } } { "data" : { "id" : 1 , "name" : "John Doe" , "email" : "john@example.com" , "createdAt" : "2024-12-15T10:30:00Z" , "updatedAt" : "2024-12-15T11:00:00Z" } , "included" : { "profile" : { "bio" : "Software Developer" , "avatar" : "/avatars/1.jpg" } } }
分页参数 # 基于偏移的分页 GET /api/v1/users?page=2&limit=20 # 基于游标的分页 GET /api/v1/users?after=eyJpZCI6IDEwMH0=&limit=20 # 时间分页 GET /api/v1/users?since=2024-12-01T00:00:00Z&until=2024-12-31T23:59:59Z
认证与授权 API 密钥认证 # 请求头中的 API 密钥 GET /api/v1/users Authorization : Bearer abc123def456X-API-Key : your-api-key-here# 查询参数中的 API 密钥 GET /api/v1/users?api_key=abc123def456
JWT 认证 # 获取 Token POST /api/v1/auth/login Content-Type : application/json{ "email": "user@example.com", "password": "password" } # 响应 { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresIn": 3600 } # 使用 Token GET /api/v1/users Authorization : Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
OAuth2 授权 # 授权码流程 GET /oauth/authorize?response_type=code&client_id=123&redirect_uri=https://app.com/callback&scope=read&state=xyz POST /oauth/token Content-Type : application/x-www-form-urlencodedgrant_type=authorization_code&code=abc123&client_id=123&client_secret=secret&redirect_uri=https://app.com/callback
版本控制 URI 版本控制 # 最常用的版本控制方式 GET /api/v1/users GET /api/v2/users # 版本特性差异 # v1: GET /api/v1/users (返回 basic info) # v2: GET /api/v2/users (返回 extended info with relationships)
请求头版本控制 # 使用 Accept 头 GET /api/users Accept : application/vnd.api+json;version=1# 使用自定义头 GET /api/users API-Version : v1
查询参数版本控制 GET /api/users?version=v1 GET /api/users?version=v2
缓存策略 缓存控制头 # 强缓存 GET /api/v1/users/123 Cache-Control : public, max-age=3600ETag : "abc123"# 条件请求 GET /api/v1/users/123 If-None-Match : "abc123"# 响应 HTTP/1.1 304 Not ModifiedGET /api/v1/users/currentCache-Control: no -cache, no -store, must-revalidate
缓存键设计 users :list:page:1:limit :20users :123:detailusers :123:detail:role:adminusers :list:filter:active:sort :name
限流与配额 限流响应头 GET /api/v1/users X-RateLimit-Limit : 1000 # 总限制X-RateLimit-Remaining : 999 # 剩余次数X-RateLimit-Reset : 1640995200 # 重置时间戳# 超限响应 HTTP/1.1 429 Too Many RequestsX-RateLimit-Limit : 1000X-RateLimit-Remaining : 0X-RateLimit-Reset : 1640995200Retry-After : 60
限流实现 from flask_limiter import Limiterfrom flask import Flaskapp = Flask(__name__) limiter = Limiter( app, key_func=lambda : request.headers.get('X-API-Key' ) or request.remote_addr ) @app.route('/api/v1/users' ) @limiter.limit("100 per hour" ) def get_users (): return jsonify(users_data) @app.route('/api/v1/premium/users' ) @limiter.limit("1000 per hour" , key_func=lambda : 'premium' ) def get_premium_users (): return jsonify(premium_users)
文档与测试 API 文档格式 openapi: 3.0 .0 info: title: User API version: 1.0 .0 paths: /api/v1/users: get: summary: Get users parameters: - name: page in: query schema: type: integer default: 1 - name: limit in: query schema: type: integer default: 20 responses: '200': description: Successful response content: application/json: schema: type: object properties: data: type: array items: $ref: '#/components/schemas/User' pagination: $ref: '#/components/schemas/Pagination' components: schemas: User: type: object properties: id: type: integer name: type: string email: type: string
API 测试 import unittestimport requestsclass TestUserAPI (unittest.TestCase): BASE_URL = "http://localhost:3000/api/v1" def test_create_user (self ): data = { "name" : "Test User" , "email" : "test@example.com" } response = requests.post(f"{self.BASE_URL} /users" , json=data) self.assertEqual(response.status_code, 201 ) self.assertIn("id" , response.json()["data" ]) def test_get_user (self ): response = requests.get(f"{self.BASE_URL} /users/1" ) self.assertEqual(response.status_code, 200 ) self.assertEqual(response.json()["data" ]["id" ], 1 )
最佳实践总结 使用名词而非动词 作为资源标识正确使用 HTTP 方法 和状态码保持 URI 简洁直观 ,避免深层嵌套提供完整的错误信息 和错误代码实施适当的缓存策略 提高性能版本控制确保向后兼容 编写详细的 API 文档 实施安全认证和限流 [!tip]
使用 HTTPS 保护所有 API 通信 实施日志记录和监控 保持 API 的一致性和可预测性 定期审查和优化 API 性能 参考资料