指南

如何构建 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 状态码,让客户端无需解析文字就能知道发生了什么:

状态码含义使用场景
200OK读取或更新成功
201CreatedPOST 创建了资源
204No Content成功,但无需返回内容
400Bad Request输入未通过校验
401Unauthorized未登录或令牌无效
403Forbidden已登录但无权限
404Not Found资源不存在,或不应让调用者知道它存在
409Conflict重复数据,或时段已被预订
429Too Many Requests触发速率限制
500Server Error服务端出现故障

错误响应应使用统一的 JSON 结构,例如 {"error": {"code": "slot_taken", "message": "该时段已不可用。"}},并且绝不在响应中包含堆栈跟踪或数据库错误信息。

选项与权衡

决策简单方案替代方案
API 放在哪里在你的网页框架中提供 API 路由独立的 API 服务,适合多个客户端或使用不同语言的情况
身份认证为自己的网页应用使用 Session Cookie为移动应用和第三方使用令牌(Bearer Token 或 API Key)
分页?page=2&limit=20基于游标(?after=...),对大型且频繁变动的列表更稳定
版本管理在有外部用户依赖之前不做版本一旦他人依赖,在路径中加入 /v1/
文档列出路由的 READMEOpenAPI 规范,工具可据此生成可交互的文档和客户端代码

分步操作

  1. 列出资源和路由,如上所述,并注明每个路由允许哪些人调用。
  2. 在数据库中建模数据,明确需要的字段和关系。
  3. 先构建读取类路由,然后是创建、更新和删除。
  4. 校验每一次写入的输入,使用模式校验库:必填字段、类型、长度、允许的取值。拒绝未知字段。
  5. 添加身份认证,再添加逐条记录的授权:访问 /bookings/42 时,必须检查预订 42 属于当前调用者,而不只是检查用户已登录。
  6. 为每个列表分页,并限制每页的最大条数。
  7. 对登录和高开销路由做速率限制。参见 速率限制。
  8. 仅当其他域名的浏览器需要调用 API 时才配置 CORS,并明确列出这些来源。如何修复 CORS 错误说明了你会遇到的错误。
  9. 为每个路由编写文档和测试,包括失败的情况。
  10. 部署,密钥放在环境变量中,启用 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 服务才有意义。

Mythex · Pricing · 用 AI 构建任何东西 · 网站和应用模板 · Mythex 对比 · AI 应用构建术语表 · 免费的 AI 应用构建工具 · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 繁體中文(香港) 繁體中文(台灣)