如何构建 REST API:设计、安全与部署
要构建 REST API,先列出应用管理的资源(用户、订单、预订等),为每个资源分配一个 URL,例如 /orders 和 /orders/{id},并用 HTTP 方法表示操作:GET 读取,POST 创建,PATCH 更新,DELETE 删除。返回带有明确状态码的 JSON,校验每一项输入,在服务器上要求身份认证并逐条检查权限,列表数据要分页。然后为接口编写文档,并把 API 部署在 HTTPS 之后。
本指南涵盖设计决策、一组完整的路由示例、给 AI 应用构建器的提示词,以及导致安全漏洞的常见错误。如果你对 API 还不熟悉,可以先阅读 什么是 API。
何时需要 REST API
- 你的网页前端需要从服务器保存和加载数据
- 移动应用、合作伙伴或自动化工具需要访问你的数据
- 其他服务需要向你发送事件(webhooks)
- 前端和后端由不同的团队分工负责
REST 是最常见的风格,但并非唯一选择。REST 与 GraphQL 对比解释了何时单一的灵活查询接口更合适。
先设计:资源与路由
写下应用中的名词,以及人们对它们的操作。以一个简单的预订应用为例:
| 方法 | 路由 | 作用 | 成功状态码 |
|---|---|---|---|
GET | /api/bookings | 列出调用者的预订(分页) | 200 |
POST | /api/bookings | 创建预订 | 201 |
GET | /api/bookings/{id} | 获取单条预订 | 200 |
PATCH | /api/bookings/{id} | 修改部分字段 | 200 |
DELETE | /api/bookings/{id} | 取消预订 | 204 |
GET | /api/health | 供监控使用的健康检查 | 200 |
这些路由对应创建、读取、更新和删除,参见 什么是 CRUD。值得遵循的约定:
- URL 使用复数名词,不用动词:用
/bookings,不用/getBookings。 - 只有在能说明归属关系时才嵌套一层:
/bookings/{id}/notes。 - 非 CRUD 的操作可以设计为子资源:
POST /bookings/{id}/cancel比用 PATCH 硬塞一个操作更清晰。 - 字段命名保持一致,例如所有地方都用
created_at。
状态码与错误
使用标准的 HTTP 状态码,让客户端无需解析文字就能知道发生了什么:
| 状态码 | 含义 | 使用场景 |
|---|---|---|
| 200 | OK | 读取或更新成功 |
| 201 | Created | POST 创建了资源 |
| 204 | No Content | 成功,但无需返回内容 |
| 400 | Bad Request | 输入未通过校验 |
| 401 | Unauthorized | 未登录或令牌无效 |
| 403 | Forbidden | 已登录但无权限 |
| 404 | Not Found | 资源不存在,或不应让调用者知道它存在 |
| 409 | Conflict | 重复数据,或时段已被预订 |
| 429 | Too Many Requests | 触发速率限制 |
| 500 | Server Error | 服务端出现故障 |
错误响应应使用统一的 JSON 结构,例如 {"error": {"code": "slot_taken", "message": "该时段已不可用。"}},并且绝不在响应中包含堆栈跟踪或数据库错误信息。
选项与权衡
| 决策 | 简单方案 | 替代方案 |
|---|---|---|
| API 放在哪里 | 在你的网页框架中提供 API 路由 | 独立的 API 服务,适合多个客户端或使用不同语言的情况 |
| 身份认证 | 为自己的网页应用使用 Session Cookie | 为移动应用和第三方使用令牌(Bearer Token 或 API Key) |
| 分页 | ?page=2&limit=20 | 基于游标(?after=...),对大型且频繁变动的列表更稳定 |
| 版本管理 | 在有外部用户依赖之前不做版本 | 一旦他人依赖,在路径中加入 /v1/ |
| 文档 | 列出路由的 README | OpenAPI 规范,工具可据此生成可交互的文档和客户端代码 |
分步操作
- 列出资源和路由,如上所述,并注明每个路由允许哪些人调用。
- 在数据库中建模数据,明确需要的字段和关系。
- 先构建读取类路由,然后是创建、更新和删除。
- 校验每一次写入的输入,使用模式校验库:必填字段、类型、长度、允许的取值。拒绝未知字段。
- 添加身份认证,再添加逐条记录的授权:访问
/bookings/42时,必须检查预订 42 属于当前调用者,而不只是检查用户已登录。 - 为每个列表分页,并限制每页的最大条数。
- 对登录和高开销路由做速率限制。参见 速率限制。
- 仅当其他域名的浏览器需要调用 API 时才配置 CORS,并明确列出这些来源。如何修复 CORS 错误说明了你会遇到的错误。
- 为每个路由编写文档和测试,包括失败的情况。
- 部署,密钥放在环境变量中,启用 HTTPS,并配置日志和健康检查。
给 AI 构建器的提示词
搭建框架:
为预订功能创建 REST API,包含以下路由:GET /api/health、GET /api/bookings(分页,每页最多 50 条)、POST /api/bookings、GET /api/bookings/:id、PATCH /api/bookings/:id、DELETE /api/bookings/:id。数据存储在我们的数据库中。使用模式校验库校验请求体,并拒绝未知字段。错误以 {"error": {"code", "message"}} 的 JSON 格式返回,并使用正确的状态码(400、401、403、404、409)。密钥保存在环境变量中。
安全:
每个预订路由都要求登录。每次读取、更新和删除时,在服务器上检查该预订是否属于当前用户;如果不属于,返回 404。绝不接受请求体中的用户 ID 或角色。对每个用户限制 POST /api/bookings 每分钟最多 20 次。在服务器端记录错误,但不向客户端返回堆栈跟踪。
文档:
为这些路由编写 OpenAPI 描述,并附一份简短的 README,为每个路由提供请求和响应示例。
测试你的 API
要从前端之外测试,因为攻击者会这样做:
- 每个路由在输入有效时都能正常工作,并返回文档中约定的结构和状态码
- 缺失或无效的字段返回 400,并附带清晰的提示信息
- 每个受保护的路由在没有令牌时返回 401
- 用户 A 通过 ID 请求用户 B 的记录时返回 404 或 403
- 列表已分页,且过大的
limit会被限制 - 重复或冲突的创建请求返回 409,而不是 500
- 触发速率限制时返回 429
- 错误响应中不包含堆栈跟踪、SQL 或密钥
- CORS 只允许你预期的来源
常见错误
- 只检查登录,不检查归属。 这是 API 最常见的漏洞:任何已登录用户只要修改 ID,就能读取任意记录。
- 信任客户端。 请求体中的价格、角色和用户 ID 都可以被篡改。
- 直接返回整行数据库记录。 密码哈希和内部字段会泄露。应明确选择每个响应包含哪些内容。
- 无限制的列表。 一次请求返回所有记录,可能拖慢整个应用。
- 返回
200 OK却在正文中包含错误信息。 客户端和监控系统无法判断请求已失败。 - 在使用 Cookie 的 API 上把 CORS 设为允许所有来源。
- 没有健康检查或日志,结果是你要等用户反馈才知道出了故障。
如需更全面的上线前检查,请参阅 AI 构建应用的安全清单。
在 Mythex 中构建 REST API
你可以在 Mythex 中要求为应用添加 API,也可以将其作为独立服务。文档中的 REST API 后端配方提供了一个起始提示词:说明你的框架和路由,要求返回清晰的 JSON 错误,把密钥保存在环境变量中,并包含 Dockerfile。Node、Python、C#、Java 等容器后端都通过 Dockerfile 发布,Mythex 会从“发布”页面处理部署(API 后端)。一个项目可以同时运行网站和 API 服务器,并可在预览中切换。将密钥保存为项目机密;如果 API 需要数据库,在对话中直接请求即可。关于围绕 API 的定时任务,请参阅如何运行后台任务。
常见问题
什么是 REST API?
REST API 是程序通过 HTTP 读写应用数据的一种方式。每类数据都是一个有自己 URL 的资源,例如 /orders,并通过标准方法对其操作:GET 读取,POST 创建,PATCH 或 PUT 更新,DELETE 删除。数据通常以 JSON 格式传输。
应该用什么语言构建 REST API?
选你或你的团队能够维护的语言即可。Node.js 配合 Express 或 Fastify、Python 配合 FastAPI、以及 C# 或 Java 的框架都很成熟。路由、状态码、校验和认证的设计规则比语言本身更重要。
如何保障 REST API 的安全?
对每个非公开路由都要求身份认证,在服务器端检查调用者是否有权访问具体的每条记录,校验所有输入,使用 HTTPS,对请求做速率限制,并把密钥保存在环境变量中。永远不要信任客户端发来的 ID 或角色信息。
REST API 一定需要单独的后端吗?
不一定。Next.js 等框架可以在页面之外直接提供 API 路由。当多个客户端(网页、移动端、合作伙伴)都要使用同一个接口,或者需要独立的语言或扩展方式时,再单独部署 API 服务才有意义。