REST API的最佳实践

我们将从这篇文章中学到什么

👉 REST API 的基本原则

  • RESTful架构的核心概念
  • 关键 HTTP 方法及其适当用途

👉 API 设计的最佳实践

  • 如何设计有意义且高效的面向资源的 URL。
  • 正确使用标准 HTTP 状态代码的重要性。
  • 实现过滤、排序和分页的技术。
  • 需要清晰、一致的 API 文档和工具来帮助实现这一点。
  • 有效的 API 版本控制策略,以确保向后兼容性。
  • 保护 API 安全的最佳实践,包括身份验证和 HTTPS。
  • 如何以用户友好且信息丰富的方式处理错误。
  • 维护 RESTful 原则和避免常见错误的实用技巧。
  • 对设计可扩展且直观的 API 的见解。
  • API 设计中的常见陷阱以及如何避免它们。

👉 实用的注意事项

  • 维护 RESTful 原则和避免常见错误的实用技巧。
  • 对设计可扩展且直观的 API 的见解。
  • API 设计中的常见陷阱以及如何避免它们。

什么是 API?

应用程序编程接口 (API) 是用于集成应用软件和服务的一组工具、定义和协议。它可以让您的产品和服务与其他产品和服务进行通信,而无需不断构建新的连接基础设施。

API 可以是私有的(仅供内部使用)、合作的(与特定合作伙伴共享以提供额外的收入来源)或公开的(允许第三方开发与您的 API 交互的应用程序以促进创新)。

https://www.redhat.com/en/topics/api/what-is-a-rest-api

什么是 REST API?

REST API(也称为 RESTful API)是一种符合 REST 架构风格约束并允许与 RESTful Web 服务交互的应用程序编程接口(API 或 Web API)。REST 代表表述性状态转移,由计算机科学家 Roy Fielding 创建。

https://www.redhat.com/en/topics/api/what-is-a-rest-api

REST API 架构约束

REST 是一组架构约束,而不是协议或标准。以下是理想 RESTful API 的约束:

  1. 客户端-服务器架构:由客户端、服务器和资源组成的客户端-服务器架构,请求通过 HTTP 进行管理。只要服务器和客户端之间的接口不变,它们也可以被替换和独立开发(也可以使用不同的编程语言)。
  2. 无状态:在客户端-服务器通信中,在处理请求传输期间,服务器上不应存储任何数据。这意味着请求之间不会存储任何客户端信息,并且每个请求都是独立且无关联的。
  3. 可缓存:客户端应该能够将响应存储在缓存中。这极大地提高了 API 的性能。
  4. 分层系统:服务器可以有多个层级来实现。这种分层架构有助于通过实现负载平衡来提高可扩展性。
  5. 按需代码:此约束是可选的。此约束表示可以通过允许从服务器下载代码并执行代码来在运行时扩展客户端应用程序的功能。
  6. 统一接口:统一接口约束是任何 RESTful 系统设计的基础。它简化并解耦了架构,使每个部分能够独立发展。

这个统一接口的四个约束是:

  1. 请求中的资源标识:使用 URI 在请求中标识单个资源。资源本身在概念上与返回给客户端的表示形式是分开的。例如,服务器可以将数据库中的数据以 HTML、XML 或 JSON 的形式发送,而这些都不是服务器的内部表示形式。

  2. 通过表示来操作资源:当客户端持有资源的表示(包括附加的任何元数据)时,它有足够的信息来修改或删除资源的状态。

  3. 自描述消息:每条消息都包含足够的信息来描述如何处理该消息。例如,可以通过媒体类型指定要调用哪个解析器。

  4. 超媒体作为应用程序状态引擎 (HATEOAS) — 访问 REST 应用程序的初始 URI 后(类似于人类 Web 用户访问网站主页),REST 客户端应该能够动态使用服务器提供的链接来发现所需的所有可用资源。随着访问的进行,服务器将使用包含指向当前可用的其他资源的超链接的文本进行响应。客户端无需使用有关服务器结构的信息进行硬编码。

来源:https://en.wikipedia.org/wiki/REST#Uniform_interface

什么是资源?

REST中信息的关键抽象是资源。任何可以命名的信息都可以是资源:文档或图像、时间服务(例如“洛杉矶今日天气”)、其他资源的集合(例如“今日畅销产品”,即产品资源的集合)、非虚拟对象(例如人)等等。

Roy Fielding 的论文

单例和集合资源

资源可以是单例,也可以是集合。

例如,“customers” 是一个集合资源,“customer” 是一个单例资源。

我们可以使用 URI 来识别 “customers” 集合资源。我们可以使用 URI 来/customers识别单个 “customer” 资源/customers/{customerId}

/customers // 是一个集合资源

/customers/{id} // 是一个单例资源

集合和子集资源

资源也可能包含子集资源。

例如,可以使用以下方式识别特定 “customers” 的子集资源 “account” /customers/{customerId}/accounts

类似地,可以识别子集合资源 “accounts” 内的单例资源 “account” /customers/{customerId}/accounts/{accountId}

/customers //是一个集合资源

/customers/{ id }/accounts //是一个子集合资源(客户的帐户)

/customers/{customerId}/accounts/{accountId} //是一个单例资源(客户的特定帐户)

URI(统一资源标识符)

URI 代表统一资源标识符(URI)。它标识网络上的逻辑或物理资源。URL 和 URN 是 URI 的子类型。URL 定位资源,而 URN 命名资源。

REST API 使用 URI 来定位资源。REST API 设计人员应创建 URI,以便将 REST API 的资源模型传达给 API 的潜在客户。如果资源命名得当,API 就会直观且易于使用。如果命名不当,同一个 API 的使用和理解就会变得困难。

URI

URI 与 URL 之间的区别(来源:https://refine.dev/blog/uri-vs-url/#introduction)

最佳实践:应该做的和不应该做的

1. 使用适当的 HTTP 方法(动词)定义 API 操作

HTTP协议定义了许多为请求赋予语义的方法。大多数 RESTful Web API 使用的常见 HTTP 方法包括:

  • GET在指定的 URI 处检索资源的表述,响应消息的主体包含所请求资源的详细信息。

  • POST在指定的 URI 处创建新资源,请求消息的主体提供了新资源的详细信息。注意:POST还可用于触发不实际创建资源的操作。

  • PUT在指定的 URI 处创建或替换资源,请求消息的主体指定要创建或更新的资源。

  • PATCH对资源执行部分更新,请求主体指定要应用于资源的一组更改。

  • DELETE删除指定 URI 处的资源。

特定请求的效果应取决于资源是集合还是单个项目。下表使用电子商务示例总结了大多数 RESTful 实现所采用的通用约定。

注意:并非所有这些请求都可能被实现——这取决于具体情况。

Resource POST GET PUT DELETE
/customers Create a new customer Retrieve all customers Bulk update of customers Remove all customers
/customers/1 Error Retrieve the details for customer 1 Update the details of customer 1 if it exists Remove customer 1
/customers/1/orders Create a new order for customer 1 Retrieve all orders for customer 1 Bulk update of orders for customer 1 Remove all orders for customer 1

POSTPUTPATCH之间的区别可能会令人困惑。

  • POST请求创建资源并将其添加到资源集合中。然后向客户端返回新创建的资源 URI。POST请求还可用于向现有资源提交数据以供处理,而无需创建任何新资源。

  • PUT请求会创建资源或更新现有资源。请求正文包含资源的完整表述。如果具有此 URI 的资源已存在,则将其替换。否则,如果服务器支持,则创建新资源。PUT请求最常应用于作为单个项目(例如特定客户)的资源,而不是集合。服务器可能支持更新但不支持通过 PUT 创建。是否支持通过 PUT 创建取决于客户端是否可以在资源存在之前有意义地为其分配URI。如果不能,则使用POST创建资源,使用PUT或PATCH进行更新。

  • PATCH请求对现有资源执行部分更新。请求正文指定要应用于资源的一组更改。这比使用PUT更高效,因为客户端仅发送更改,而不是资源的整个表示。

PUT请求必须是幂等的。如果客户端多次提交相同的PUT请求,则结果应该始终相同(将使用相同的值修改相同的资源)。POST和PATCH请求不保证是幂等的。

2. 使用名词(复数名词)表示资源

RESTful URI 应该引用作为事物(名词)的资源,而不是引用动作(动词),因为名词具有动词所不具备的属性 — 同样,资源也具有属性。以下是一些资源示例:

  • 用户(user)
  • 帐户(account)

其资源URI可以设计如下:

HTTP Method Endpoint Description
GET /accounts Returns a list of accounts
GET /accounts/{accountNumber} Returns the account with account number {accountNumber}
GET /users Returns a list of users
GET /users/{id} Returns the user with ID {id}
GET /orders Good - Returns a list of orders
GET /create-order Avoid - verb or action-based names
GET /students Good - Returns a list of students
GET /student Avoid - singular names

3.使用子资源建立关系

使用一致的资源命名约定和 URI 格式,以最大程度地减少歧义并实现最大的可读性和可维护性。实施以下设计提示可实现一致性:

  • 在 REST 中,关系通常由子资源建模。对子资源使用以下模式。
HTTP Method Endpoint Description
GET /{resource}/{resource-id}/{sub-resource} Retrieves a list of sub-resources
GET /{resource}/{resource-id}/{sub-resource}/{sub-resource-id} Retrieves a specific sub-resource
POST /{resource}/{resource-id}/{sub-resource} Creates a new sub-resource
GET /post/{post-id}/comments Retrieves a list of comments for a post
GET /post/{post-id}/comments/{comment-id} Retrieves a specific comment for a post
POST /post/{post-id}/comments Creates a new comment for a post
GET /cars/711/drivers/ Returns a list of drivers for car 711
GET /cars/711/drivers/4 Returns driver #4 for car 711

4.注重可读性和一致性

  • 不要在 URI 中使用尾部斜杠 (/)
GET /device-management/managed-devices/ 
GET /device-management/managed-devices /*✅这是一个更好的版本*/
  • 使用连字符 (-) 和短横线命名 ( some-var-name) 来提高 URI 的可读性
GET /devicemanagement/manageddevices/ 
GET /device-management/managed-devices /*✅这是一个更好的版本* / 

GET /m y -folder/ my -doc // ✅ 推荐
GET /MY-FOLDER/ my -doc // ❌ 避免
GET /My-Folder/ my -doc // ❌ 避免
  • 避免使用下划线 ( _ ) 和驼峰式命名 ( someVarName) 蛇形命名 ( some_var_name) 帕萨尔式命名 ( SomeVarName)
GET /managed-entities/{ id }/install-script-location // ✅ 更易读

GET /managed_entities/{ id }/install_Script_Location // ❌ 蛇形命名法- 易读但不常规

GET /managedEntities/{ id }/installScriptLocation // ❌ 驼峰命名法- 可读性较差
  • 不要使用文件扩展名
/device-management/managed-devices.xml /*❌请勿使用它*/ 

/device-management/managed-devices /*✅这是正确的 URI*/
  • 切勿在 URI 中使用CRUD函数名称
/* ❌ 避免 */

HTTP GET / get -managed-devices      
HTTP POST /create-managed-devices 


/* ✅ 推荐 */

HTTP GET /managed-devices          // 获取所有设备
HTTP POST /managed-devices          // 创建新设备

HTTP GET /managed-devices/{id}     // 获取给定 Id 的设备
HTTP PUT /managed-devices/{id}     // 更新给定 Id 的设备
HTTP DELETE /managed-devices/{id}     // 删除给定 Id 的设备
  • 不要在 URI 中使用动词
GET /scripts/{id}/execute    // ❌ 不要这样做!- 这是 RPC,而不是 REST

POST /scripts               // ✅ 推荐:使用 POST 操作创建脚本

GET /scripts/{id}/status    // 检查执行状态

5.使用HTTP响应状态码

始终使用常规 HTTP 状态码来响应对 API 发出的请求。这将帮助用户了解发生了什么 — 请求是否成功,是否失败,或者其他情况。

HTTP状态码

6.使用过滤、排序和分页来检索请求的数据

Feature Description Example Usage
Sorting Allows sorting of the dataset based on specified parameters. /companies?sort=rank_asc
Filtering Filters the dataset based on given criteria in query parameters. /companies?category=banking&location=india
Searching Searches for specific terms within the dataset. /companies?search=Digital
Pagination Divides the dataset into smaller chunks for easier handling and improved performance. /companies?page=23

7. 使用 HATEOAS 实现到相关资源的导航

HATEOAS(超文本作为应用程序状态引擎)是 REST 的一个原则,允许客户端仅通过响应中提供的超链接来浏览资源并发现可用的操作。这意味着客户端不需要事先了解 URI;相反,每个 HTTP GET 请求都会返回必要的信息和相关资源的链接,从而实现对 API 的动态探索。

在示例响应中,/books/1分享可导航的链接,例如/authors/1 /books/1/reviews

{ 
  “books” : [ 
    { 
      “id” : 1 ,
      “title” : “了不起的盖茨比” ,
      “author” : “F·斯科特·菲茨杰拉德” ,
      “links” : { 
        “self” : “/books/1” ,
        “author” : “/authors/1” ,
        “reviews” : “/books/1/reviews” 
      } 
    } 
    { 
      “id” : 2 ,
      “title” : “杀死一只知更鸟” ,
      “author” : “哈珀·李” ,
      “links” : { 
        “self” : “/books/2” ,
        “author” : “/authors/2” ,
        “reviews” : “/books/2/reviews” 
      } 
    } 
  ] 
}

8. 使用 SSL 确保安全

SSL(安全套接字层)对于保护 REST API 免受恶意攻击至关重要。它确保服务器和客户端之间的通信是私密的,并且用户只能访问他们有权查看的数据。

https:// 我们可以通过查看URL来判断 REST API 是否受 SSL 保护。例如,https://mysite.com/posts 受 SSL 保护,而 http://mysite.com/posts 不受 SSL 保护。

9.提供准确的API文档

良好的 API 文档可帮助开发人员正确使用我们的 API。它可以让更多人想要使用我们的 API 并更好地使用它。一个流行的 API 文档工具是OpenAPI ( Swagger )

10. RESTful Web API 版本控制

Versioning Method Description Example Usage Real-World Examples
URI Path Includes the version number directly in the API path. http://www.example.com/api/v1/productshttp://www.example.com/api/v2/products - Twitter- PayPal- Google Translate
Query Parameters Specifies the version number as a query parameter in the URL. http://www.example.com/api/products?version=1http://www.example.com/api/products?version=2 - Google Translation APIs
Custom Headers Uses custom headers to specify the API version. http://localhost:8080/api/productsHeaders: X-API-VERSION=1http://localhost:8080/api/productsHeaders: X-API-VERSION=2 -
Content Negotiation Uses the Accept header to specify the version of the API. http://localhost:8080/api/productsHeaders: Accept=application/vnd.javaguides-v1+jsonhttp://localhost:8080/api/productsHeaders: Accept=application/vnd.javaguides-v2+json - GitHub

11. API 安全和访问控制

确保 API 安全并控制谁可以使用它是制作优质 RESTful API 的关键部分。以下是一些实现此目的的方法:

  • 设置 OAuth 2.0JSON Web Tokens (JWT),以便未经身份验证的用户无法访问

  • API 密钥显示谁在使用。API速率限制可阻止人们过度使用。

  • 实现基于角色的访问控制(RBAC)等。

12. 不要忘记 API 性能

让我们的 API 更好地发挥作用是让用户满意、降低成本和更轻松地发展的关键。

  • 有效缓存

  • 数据压缩方法

  • 异步处理

  • 处理批处理操作

  • 测量 API 性能并分析 API 使用情况

  • 监控 API 健康和正常运行时间

  • 测试 RESTful API(单元测试、集成测试、负载测试、安全测试)等。

结论

优秀的 REST API 是通过遵循关键规则和最佳实践来构建的。

通过遵守这些规则,我们将构建易于使用、可靠且面向未来的 API。

注:本文译自 https://medium.com/@syedabdullahrahman/mastering-rest-api-design-essential-best-practices-dos-and-don-ts-for-2024-dd41a2c59133