发布于 2025-12-08 1 阅读
0

设计生产就绪、开发人员友好的 RESTful API 的基本指南

设计生产就绪、开发人员友好的 RESTful API 的基本指南

作者:Paramananham Harrison✏️

开发者是编程 API 的主要用户。我们常常关注产品的 UI 和 UX,却忽略了为 API 打造良好 UX 的重要性。

它可能不会在产品开发的初始阶段造成问题,但一旦它被多个开发人员出于不同需求而使用,它很容易成为开发和产品执行速度的瓶颈。

在这篇文章中,我们将讨论如何避免这个问题并确保您的 API 随着产品的增长而顺利扩展。

我们将讨论一些最佳实践和指南,以便为 API 构建更好的 UX,尤其是广泛使用的 RESTful API。

这并不是一份指南,告诉你“这是构建 REST API 的最佳方式”。每个产品都有不同的需求——这些只是一些通用的指导原则,旨在让你的 REST API 获得更好的 DX(开发者体验)。

REST API 设计基础

盲目遵循 Web 标准并不能创建出优秀的 API。RESTful 是一种灵活的 API 架构风格。它不会规定如何创建,而只是告诉你在设计过程中需要注意哪些方面。

以下是 REST API 设计的一些基本技巧:

  • 从资源的角度思考——而不是 CRUD 操作
  • 使用适当的 HTTP 动词
  • 制作不言自明的 URL
  • 发送适当的内容类型作为标题
  • 使用正确的 HTTP 状态代码
  • 正确处理错误并发送客户端错误的错误消息

在这篇文章中,我们将按照这些指导方针为工作板创建一个模拟 API。

从资源角度思考

REST API 的核心在于创建资源。本质上,资源是应用程序的逻辑拆分。

它不需要与你的数据模型相同。因为你可以在多个数据模型中使用资源,所以它与 CRUD 不同。

例如,在我们的工作板上,我们可以拥有多个资源,其中一些资源在其操作中使用多个数据模型。

  • 工作
  • 使用的数据模型:职位、类别、职位类型
  • 公司
  • 使用的数据模型:公司、用户、订单
  • 应用
  • 使用的数据模型:应用程序、用户

在这些资源中,将有多种操作——不仅仅是数据模型的 CRUD。在下一节中,我们将探讨如何使用 HTTP 动词和 URL 来分离这些操作。

HTTP 动词和 URL

HTTP 动词有很多种,例如 GET、POST、PUT、PATCH、DELETE。所有这些 HTTP 动词都有特定的功能。

除了这些 HTTP 动词之外,资源还可以具有多种功能。

例如:

  • GET /jobs– 检索所有作业
  • GET /jobs/1234– 使用 JobID 检索特定作业1234
  • POST /jobs– 创建新的职位列表
  • PUT /jobs/1234– 使用 JobID 更新作业1234
  • DELETE /jobs/1234– 删除具有 JobID 的作业1234
  • PATCH /jobs/1234– 使用 JobID 更新作业的部分内容1234。它与 类似PUT,但 put 会更新整个作业,而PATCH更新作业数据的特定部分。

更好的 URL 架构

一个小提示:不要像这样构造 URL:

  • POST /createJobs创造一份工作❌
  • GET /getAllJobs获取所有作业❌
  • GET /getJobById获取具有 ID 的特定工作❌

这种方法可行,而且它也是一个 REST API。没有规定说你不能这样使用 REST API。

然而,这种方法的可扩展性不佳。

对于使用它的开发人员来说,这将是一场噩梦,他们每次都需要查看文档来检查特定操作所需的 URL 模式。

我建议使用名词来表示资源 URL,而不是动词。这样用户一眼就能知道需要更新和删除的 URL。

POST /jobs– 创建作业✅

GET /jobs – 检索所有作业✅

使用此 URL 模板将帮助开发人员轻松理解他们需要发送删除请求来/jobs/:id删除工作。

明确发送内容类型标头

如果 URL 中未明确指定内容类型,则始终发送默认内容类型。

如今,JSON 是默认内容类型,并发送内容类型的标头,以便用户知道 API URL 返回什么类型的内容。

一些内容类型标题包括以下内容:

  • 内容类型:application/json
  • 内容类型:text/html
  • 内容类型:application/xml

小心处理嵌套资源

资源通常具有多种关系,因此我们可能需要通过嵌套资源来获取这些关系。如果嵌套资源定义不正确,这可能会很棘手。

在我们的招聘公告板示例中,一个职位可以有多个申请。您可以通过职位资源本身获取这些申请。

例如:

  • GET /jobs/1234/applications– 获取特定 jobID 的所有申请 ( 1234)
  • GET /jobs/1234/applications/:123123– 获取jobID 为 ( ) 的作业对应的 applicationID 为 ( 1234) 的特定应用程序
  • /companies/12345/applications– 获取特定公司的所有申请(12345)。

在这里您可以看到两者都Jobs与资源Companies有关系Applications。

在这种情况下,不建议通过嵌套资源创建新的应用程序。

相反,通过嵌套资源进行检索并通过Applications资源创建新的应用程序。

换句话说,使用POST /applications来创建一个新的应用程序,其中将包含有关特定作业的信息。

在某些情况下,这是最有效的方法,但并非总是如此。最终,这取决于具体用例。

如果申请的唯一直接联系对象是工作机会而非公司,那么这种方法是可行的。您可以在 中创建求职申请POST /jobs/1234/applications。

尽管如此,分离资源并尽可能避免嵌套总是好的。

一般来说,尽量不要超过一层嵌套,并确保在逻辑上分成单独的资源。

支持过滤以避免嵌套资源

在我们的用例中,使用过滤可以帮助我们避免嵌套:

  • GET /applications?jobId=1234– 这将获取具有 ID 的特定职位的所有申请
  • GET /applications?companyId=12345– 这将获取特定公司 ID 的所有申请

过滤器也可以基于字段:

  • GET /jobs?jobType=Remote– 这将获取以下职位:jobType: Remote
  • GET /jobs?categories=developers,designers,marketers– 过滤器可以是数组。在本例中,它会过滤类别中的所有职位developers,designers并且marketers

支持搜索

有两种类型的搜索:

  • 基于字段的搜索
  • 通用搜索

常规搜索可以作为查询字符串传递,并使用q或search作为键。

例如:/jobs?q=searchterm

基于字段的搜索与基于字段的过滤相同。

有些字段会根据完全匹配进行筛选,而有些字段则会根据部分正则表达式进行筛选。

例如:/jobs?title=marketing ninja。在这里,我们可以搜索部分标题为marketing ninja

使用适当的 HTTP 状态代码并在整个 API 中一致使用它

我们都知道特定的 HTTP 状态代码意味着什么——200、4xx、5xx、302 等。

我们使用这些状态码来让 API 使用者确切地了解处理其请求的具体情况。持续使用状态码是良好 API 用户体验的关键。

需要注意的是,您不需要支持所有 HTTP 状态代码,但您应该尝试支持与您的 API 需求相符的 HTTP 状态代码。

您肯定不想发送Not found状态码为 的错误200。这是一种不好的做法,会让用户搞不清楚是否发生了错误。

以下是 API 中 HTTP 状态代码的一些示例:

  • 获取、放置、修补 – 200 OK
  • POST – 201 创建
  • 删除 – 204 无内容

以下是一些错误的状态代码:

  • 400——错误请求
  • 401 – 未授权
  • 404 – 未找到
  • 429——请求过多
  • 500——内部服务器错误

错误消息和响应

在响应中发送客户端错误的详细信息也是一个好主意,这样 API 用户可以向最终用户显示错误详细信息。

具有正确错误响应的示例响应如下:

// A sample response
{
  errors: [{
    'status': 'InvalidError'
    'message': 'Invalid value for email',
    ... // Other details of the error
  }, {
    ... // Next error object
  }],
  data: {
  ... // Any data
  }
}
Enter fullscreen mode Exit fullscreen mode

异步响应

如果 API 操作正在后台执行异步操作,请立即向用户发送响应。不要等到该过程结束后再发送带有相应状态代码的响应。

通常,202 Accepted在这种情况下你会使用。这并不意味着操作已完成——只是它已被接受。

电子邮件触发器和大量计算都是异步操作。

选择字段:允许客户端获取他们真正想要的内容

允许您的 API 用户选择所需的字段。默认情况下,向他们发送所有相关数据。

如果用户明确要求提供具体信息,则仅发送请求的详细信息。这样,您的 API 就能灵活地发送客户端所需的确切数据。

例子:

  • GET /jobs?fields=id,title,description,jobType,categories– 这仅显示明确传递给字段查询字符串的字段内的作业。

按需扩展资源

数据模型具有多个模型的 ID 引用。如果您的响应时间较慢,请不要在解析资源时默认从多个模型中展开对象。

例如,以下代码片段显示了以 jobType 和 categories 作为 ID 的作业响应:

// GET /jobs
[{
  title: 'Job title',
  description: 'Job description',
  jobType: 1233043949238923, // ID ref to jobType model
  categories: [ // ID ref to categories model
    1029102901290129,
    0232392930920390,
  ]
},
{
... // Job Objects
}]
Enter fullscreen mode Exit fullscreen mode

接下来,我们将使用显式请求扩展 jobType 和 Categories 数据:GET /jobs?expand=jobType,categories

// GET /jobs?expand=jobType,categories
[{
  title: 'Job title',
  description: 'Job description',
  jobType: 'Remote', // Resolved from jobType model
  categories: [ // Resolved from categories model
    {
      name: 'Front end developer' 
    },
    {
      name: 'React developer'
    },
  ]
},
{
... // Job Objects
}]
Enter fullscreen mode Exit fullscreen mode

支持排序,为前端提供更多灵活性

默认情况下,每个资源都有不同的排序顺序。因此,最好为 API 用户提供基于字段排序的灵活性。支持升序和降序响应相当容易。

例如:

  • GET /jobs?sort=createdDatecreatedDate– 这只是按升序对响应进行排序
  • GET /jobs?sort=-createdDate– 按相反顺序(降序)排序
  • GET /jobs?sort=-createdDate,title– 按多个值排序(createdDate 按降序排列,title 按升序排列)

您无需遵循相同的约定,这完全取决于您使用的框架。这只是一个如何支持资源排序的通用示例。

明智地使用分页

对于较小的资源,您不需要使用分页。

但是,一旦响应超过一定大小,分页就派上用场了。请确保分页实现简单明了。

例如:

  • GET /jobs?page=2&size=10– 此处,page“页码”表示页码,“size”表示每页作业数量限制。在本例中,第 2 页包含作业 11 到 20 个。

在响应中,我们将向 API 用户发送相关页面信息以及内容:

// Sample paginated list example
  {
    data: [
      {
        ... // actual response data
      }
    ],
    pageInfo: {
      currentPage: 2,
      hasNextPage: false,
      hasPrevPage: true,
      ... // Add any more pagination related information
    }
  }
Enter fullscreen mode Exit fullscreen mode

到目前为止,我们已经介绍了创建 REST API 所需了解的最低限度的概念。

现在我们将转换话题并讨论一些用于创建开发人员友好、可用于生产的 RESTful API 的高级概念。

在 API 的早期阶段使用 HATEOAS

开发人员经常讨厌 HATEOAS,不仅仅是因为名字本身就带着“恨”。我不会深入解释 HATEOAS 是什么——我只是想告诉你它的作用。

HATEOAS 是一种将所有相关资源 URL 显式发送到您的端点的方法。它允许消费者轻松地在您的资源之间导航,而无需自行构建 URL。

这是 RESTful API 背后的主要概念之一。它允许 API 用户了解对任何给定资源及其相关资源的不同操作。

例如:

GET /jobs– 获取所有作业。

它对 HATEOAS 的响应如下所示:

// HATEOAS links are in the links section
{
  data: [{...job1}, {...job2}, {...job3}, ...],
  links: [
    // GET all applications
    {
      "rel": "applications",
      "href": "https://example.com/applications",
      "action": "GET",
      "types": ["text/xml","application/json"]
    },
    {
      "rel": "jobs",
      "href": "https://example.com/jobs",
      "action": "POST",
      "types": ["application/json"]
    },
    {
      "rel": "jobs",
      "href": "https://example.com/jobs",
      "action": "DELETE",
      "types": []
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

所有相关链接都添加到响应本身。它可以帮助 API 用户在资源和不同操作之间导航。

身份验证和授权

在允许用户完成任何会改变数据的操作之前,务必对用户进行身份验证和授权。

您还应该通过授权墙来限制对所有敏感信息的访问。只有未完成必要身份验证和授权的用户才能访问公开信息。

以下是身份验证和授权过程中需要牢记的一些提示:

  • 实施 RBAC(基于角色的访问控制)并允许用户拥有多个角色
  • 为每个角色提供细粒度的权限,并在用户级别允许某些权限
  • 始终进行身份验证,然后检查用户是否有权执行该操作。如果没有授权,则发送403 forbidden响应。
  • 如果用户未通过身份验证,则发送401 Unauthorized响应
  • 对于无效凭证,发送401 Unauthorized响应

API 安全

安全是一个广泛的话题。在 API 层面,最佳实践是:

  • 始终验证请求数据
  • 遵循拒绝第一原则,仅当 API 请求通过特定端点的所有检查时才允许
  • 不允许在没有适当验证的情况下通过 API 进行批量操作
  • 编写集成测试和一些端到端测试,以确保 API 操作的可靠性

当你需要对 API 进行重大更改时,版本控制可以帮你节省时间

API 是用户与开发者之间的契约。当你对架构进行重大更改时,很容易忘记契约,从而破坏现有 API 客户端的功能。

这就是 API 版本控制的作用所在。

例如:

  • GET /v1/jobs– 获取 API 版本 1 并发送 XML 响应
  • GET /v2/jobs– 默认发送 JSON 响应

这样,我们就不会破坏现有用户的 API。相反,我们可以在必要时显示弃用警告,并要求现有用户使用新版本的 API。

版本控制还可以通过其他几种方式为您提供帮助:

  • 它允许您发布实现的 Beta 版本
  • 它让你的 API 用户有时间适应任何变化

一些广泛使用的版本控制方法的示例包括基于数字和基于日期的版本控制。

最后,版本控制不必在 URL 上。某些 API(例如 Github REST)会将版本控制作为自定义标头传递:

接受:application/vnd.github.v3+json

  • v3 是 REST API
  • v4 是 github 的 GraphQL API

必要时限制速率

大多数 API 不需要速率限制,但它可以为您的 API 添加一些基本的安全性。

速率限制有多个级别:

  • 基于特定时间段内的请求数量进行速率限制(基于窗口的速率限制)。当指定时间到期时,速率限制会自动重置。
  • 基于信用额度的速率限制,用户需要充值才能再次使用。如果用户尚未充值信用额度,则会收到错误消息。
  • 通过自定义标头发送有关速率限制的信息,以便客户端知道他们在窗口期内或当前信用额度内还剩下多少个请求。

Github 对其 API 进行速率限制的方式如下:

curl -i https://api.github.com/users/octocat
HTTP/1.1 200 OK
Date: Mon, 01 Jul 2013 17:27:06 GMT
Status: 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 56
X-RateLimit-Reset: 1372700873
This way, you don’t need to fetch from DB every time.
Enter fullscreen mode Exit fullscreen mode

现代数据库针对读取进行了优化,因此缓存并非总是必要的。不过,尽可能使用缓存有助于提高读取速度。

虽然缓存很有价值,但它会增加 API 的复杂性,因为每当数据发生变化时您都需要进行缓存并重新缓存。

如果数据没有改变,服务器应该返回304 Not Modified。此响应将向浏览器客户端表明数据没有改变,并提示服务器重用之前获取的旧数据。

实现 CORS

CORS 允许跨域访问 API。大多数应用程序只需将某些域名列入白名单,即可允许来自这些域名的 CORS。

对于公共 API,您可能需要允许任何人(如果他们设置了正确的身份验证密钥)获取数据。在这种情况下,请实施 CORS 以允许所有域,并在可疑域中将其列入黑名单。

当你遇到麻烦时,日志会拯救你

日志记录是任何 Web 平台开发中不可或缺的一部分。对于 API 来说也是如此——我们需要根据优先级(错误、信息、警告)对日志进行分类。

当出现错误和安全问题时,正确的日志记录和分离将加快以后的调试。

请记住以下提示以确保您的日志尽可能高效:

  • 尝试遵循一些日志记录标准(例如:JSON 日志)。使用日志记录框架有助于促进标准化,从长远来看可以节省大量时间。
  • 尝试在日志上创建警报和分析模式来识别问题
  • 不要将所有错误都升级到同一优先级范围内。在 API 中按优先级对每个错误进行分类之前,请先检查受影响的用户数量以及问题的严重程度。日志记录应该有助于识别这些模式。
  • 确保记录所有请求、会话以及有关请求来源的详细信息,以便评估任何与安全相关的问题

监控设置

监控设置时需要记住以下几点提示:

  • 投资良好的监控设置
  • 显示 API 的状态页面
  • 确保你的支持渠道易于获取。通过 Twitter 提供后续跟进也是一个好主意——这能为那些想要查找简单问题答案的人节省大量时间。
  • 监控响应时间
  • 检查慢查询并尝试优化它们

面向其他开发人员的 API 文档

在为开发人员开发 API 文档时,确保所有内容都是最新的非常重要:

  • 更新 API 文档以及您的拉取请求,并尽可能包含文档的版本控制
  • 记录开发 API 过程中做出的细微决策,并将其添加到发布说明中。这可确保所有使用同一 API 的人员都了解每个决策背后的原因。此外,它还能帮助团队自主工作。

Postman 集合和 Swagger API 文档是开发人员文档的很好的例子。

消费者文档

公开API文档如下:

  • 清晰地了解你的资源
  • 显示有关限制以及如何避免滥用 API 的详细信息
  • API 游乐场将增强体验,并有助于直接测试功能,而无需复杂的设置
  • 在必要时显示警告

如果您想了解优秀的 API 文档,请查看以下来源:

选择正确的框架,不要事事亲力亲为

您可以将这最后一条建议应用到您正在进行的任何开发项目中,包括 API 开发。

一般来说,重用开源框架来为消费者构建可靠的 API 比重新发明轮子更容易。

结论

本指南是构建出色的 API 用户体验的起点。

很多时候,我们只需要构建一个快速的API,这个API可能不会被普通大众使用。

确保你的 API 能够触达用户,只实现当前产品水平所需的功能,然后根据需要进行扩展。过早优化永远不是一个好主意。

请在评论中随意分享您对构建 API 的见解和经验。


编者注:觉得这篇文章有什么问题?您可以在这里找到正确版本。

插件:LogRocket,一个用于 Web 应用的 DVR

 
LogRocket 仪表板免费试用横幅
 
LogRocket是一款前端日志工具,可让您重播问题,就像它们发生在您自己的浏览器中一样。无需猜测错误发生的原因,也无需要求用户提供屏幕截图和日志转储,LogRocket 让您重播会话,快速了解问题所在。它可与任何应用程序完美兼容,不受框架限制,并且提供插件来记录来自 Redux、Vuex 和 @ngrx/store 的额外上下文。
 
除了记录 Redux 操作和状态外,LogRocket 还记录控制台日志、JavaScript 错误、堆栈跟踪、带有标头 + 正文的网络请求/响应、浏览器元数据以及自定义日志。它还会对 DOM 进行插桩,以记录页面上的 HTML 和 CSS,即使是最复杂的单页应用程序,也能重现像素完美的视频。
 
免费试用。


设计可用于生产且对开发人员友好的 RESTful API 的基本指南一文最先出现在LogRocket 博客上。

链接:https://dev.to/bnevilleoneill/the-essential-guide-for-designing-a-production-ready-developer-friendly-restful-api-1io