如何设计更好的接口
2022-3-10
| 2023-6-6
字数 2976阅读时长 8 分钟
APIs are awesome, but they’re also extremely hard to design. When creating an API from scratch, you need to get many details right. From basic security considerations to using the right HTTP methods, implementing authentication, deciding which requests and responses you should accept and return, … the list goes on.
Api 很棒,但是设计起来也非常困难。在从头创建 API 时,您需要获得许多正确的细节。从基本的安全考虑到使用正确的 HTTP 方法,实现身份验证,决定应该接受和返回哪些请求和响应… … 这个列表还在继续。
In this post, I’m trying my best to compress everything I know about what makes a good API. An API, that your consumers will enjoy using. All tips are language-agnostic, so they apply to any framework or technology.
在这篇文章中,我尽我所能压缩我所知道的关于如何创建一个好的 API 的所有信息。一个 API,你的消费者会喜欢使用它。所有的技巧都与语言无关,因此它们适用于任何框架或技术。

1. Be consistent

1. 保持一致

I know it sounds logical, but it’s hard to get this one right. The best APIs I know are predictable. When a consumer uses and understands one endpoint, they can expect that another endpoint works the same way. This is important for the entire API and one of the key indicators of whether or not an API is well-designed and great to use.
我知道这听起来很合乎逻辑,但这个问题很难回答对。我所知道的最好的 api 是可以预测的。当消费者使用和理解一个端点时,他们可以期待另一个端点以同样的方式工作。这对于整个 API 来说非常重要,也是判断一个 API 是否设计良好、使用方便的关键指标之一。
  • Use the same casing for fields, resources, and parameters (I prefer 对字段、资源和参数使用相同的大小写(我更喜欢这样)snake_case)
  • Use either plural or singular resource names (I prefer plural) 使用复数或单数资源名称(我更喜欢使用复数)
    • /users/{id}, /orders/{id} or 或/user/{id}, /order/{id}
  • Use the same authentication and authorization methods for all endpoints 对所有端点使用相同的身份验证和授权方法
  • Use the same HTTP headers across the API 跨 API 使用相同的 HTTP 头
    • For example 例如Api-Key for passing an API key 传递 API 密钥
  • Use the same HTTP status codes based on the type of response 根据响应类型使用相同的 HTTP 状态代码
    • For example 例如404 when a resource can not be found 当资源找不到的时候
  • Use the same HTTP methods for the same kind of actions 对同类操作使用相同的 HTTP 方法
    • For example 例如DELETE when deleting a resource 当删除一个资源

2. Use ISO 8601 8601 UTC dates

2. 使用世界协调时日期

When dealing with date and time, APIs should always return ISO 8601-formatted strings. Displaying dates in a specific time zone is generally a concern of client applications.
在处理日期和时间时,api 应该始终返回 ISO 8601 格式的字符串。在特定时区显示日期通常是客户端应用程序关心的问题。

3. Make an exception for public endpoints

3. 为公共端点破例

Every endpoint should require authorization by default. Most endpoints require an authenticated user to be called, so making this the default makes sense. If an endpoint needs to be called publicly, explicitly set this endpoint to allow unauthorized requests.
默认情况下,每个端点都需要授权。大多数端点都需要调用经过身份验证的用户,因此将其设置为默认值是有意义的。如果需要公开调用某个端点,请显式设置该端点以允许未经授权的请求。

4. Provide a health check endpoint

4. 提供一个健康检查终点

Provide an endpoint (for example GET /health) that determines whether or not a service is healthy. This endpoint can be called by other applications such as load balancers to act in case of a service outage.
提供一个端点(例如 GET/health) ,用于确定服务是否健康。在服务中断的情况下,负载均衡器等其他应用程序可以调用这个端点。

5. Version the API

5. 对 API 进行版本化

Make sure you version your API and pass the version on each request so that consumers aren’t affected by any changes to another version. API versions can be passed using HTTP headers or query/path parameters. Even the first version of the API (1.0) should be explicitly versioned.
确保您对 API 进行了版本化,并在每个请求上传递了版本,这样使用者就不会受到对另一个版本的任何更改的影响。API 版本可以使用 HTTP 头或查询/路径参数传递。即使是 API (1.0)的第一个版本也应该显式地进行版本控制。
Some examples:
以下是一些例子:
  • https://api.averagecompany.com/v1/health
  • https://api.averagecompany.com/health?api_version=1.0

6. Accept API key authentication

6. 接受 API/密钥认证

If an API needs to be called by a third party, it makes sense to allow authentication via API keys. API keys should be passed using a custom HTTP header (such as Api-Key). They should have an expiration date, and it must be possible to revoke active keys so that they can be invalidated in case they get compromised. Avoid checking API keys into source control (use environment variables instead).
如果需要第三方调用 API,那么允许通过 API 密钥进行身份验证是有意义的。应该使用自定义 HTTP 标头(如 API-key)传递 API 密钥。它们应该有一个过期日期,并且必须可以撤销活动密钥,以便在密钥泄露的情况下使其失效。避免将 API 键检查到源代码管理中(使用环境变量代替)。

7. Use reasonable HTTP status codes

7. 使用合理的 HTTP 状态代码

Use conventional HTTP status codes to indicate the success or failure of a request. Don’t use too many, and use the same status codes for the same outcomes across the API. Some examples:
使用传统的 HTTP 状态代码来指示请求的成功或失败。不要使用太多,并且在整个 API 中对相同的结果使用相同的状态代码。以下是一些例子:
  • 200 for general success 为了一般的成功
  • 201 for successful creation 成功的创造
  • 400 for bad requests from the client 因为客户提出了不好的要求
  • 401 for unauthorized requests 未经授权的请求
  • 403 for missing permissions 丢失许可证
  • 404 for missing resources 寻找丢失的资源
  • 429 for too many requests 要求太多了
  • 5xx for internal errors (these should be avoided at all costs) 内部错误(应该不惜一切代价避免这些错误)

8. Use reasonable HTTP methods

8. 使用合理的 HTTP 方法

There are many HTTP methods, but the most important ones are:
有许多 HTTP 方法,但最重要的是:
  • POST for creating resources 来创造资源
    • POST /users
  • GET for reading resources (both single resources and collections) 用于阅读资源(单个资源和集合)
    • GET /users
    • GET /users/{id}
  • PATCH for applying partial updates to a resource 用于对资源应用部分更新
    • PATCH /users/{id}
  • PUT for applying full updates to a resource (replaces the current resource) 用于对资源应用完整更新(替换当前资源)
    • PUT /users/{id}
  • DELETE for deleting resources 删除资源
    • DELETE /users/{id}

9. Use self-explanatory, simple names

9. 使用不言自明的简单名字

Most endpoints are resource-oriented and should be named that way. Don’t add unnecessary information that can be inferred from elsewhere. This also applies to field names.
大多数端点是面向资源的,应该以这种方式命名。不要添加可以从其他地方推断出来的不必要的信息。这也适用于字段名。
GOOD
很好
  • GET /users => Retrieve users = > 检索用户
  • DELETE /users/{id} => Delete a user = > 删除用户
  • POST /users/{id}/notifications => Create a notification for a specific user = > 为特定用户创建通知
  • user.first_name
  • order.number
BAD
Something BAD
  • GET /getUser
  • POST /updateUser
  • POST /notification/user
  • order.ordernumber
  • user.firstName

10. Use standardized error responses

10. 使用标准化的错误响应

Aside from using HTTP status codes that indicate the outcome of the request (success or error), when returning errors, always use a standardized error response that includes more detailed information on what went wrong. Consumers can always expect the same structure and act accordingly.
除了使用 HTTP 状态代码指示请求的结果(成功或错误)之外,在返回错误时,始终使用标准化的错误响应,其中包含关于出错原因的更详细的信息。消费者总是可以期待相同的结构并采取相应的行动。

11. Return created resources upon POST

It’s a good idea to return the created resource after creating it with a POST request. That’s mostly important because the returned, created resource will reflect the current state of the underlying data source and will contain more recent information (such as a generated ID).
在使用 POST 请求创建已创建的资源之后返回该资源是一个好主意。这一点非常重要,因为返回的、创建的资源将反映基础数据源的当前状态,并且将包含更新的信息(例如生成的 ID)。

12. Prefer PATCH over PUT

12. 更喜欢 PATCH 而不是 PUT

As mentioned earlier, PATCH requests should apply partial updates to a resource, whereas PUT replaces an existing resource entirely. It’s usually a good idea to design updates around PATCH requests, because:
如前所述,PATCH 请求应该对资源应用部分更新,而 PUT 则完全替换现有资源。围绕 PATCH 请求设计更新通常是个好主意,因为:
  • When using 使用时PUT to update only a subset of fields for a resource, the entire resource still needs to be passed, which makes it more network-intensive and error-prone 为了只更新资源的字段子集,仍然需要传递整个资源,这使得它更加网络密集和容易出错
  • It’s also quite dangerous to allow any field to be updated without any restrictions 允许任何字段在没有任何限制的情况下进行更新也是相当危险的
  • From my experience, there barely exist any use cases in practice where a full update on a resource would make sense 根据我的经验,在实践中几乎不存在任何资源的完整更新有意义的用例
  • Imagine an 想象一下order resource that has an 资源,有一个id and a 和一个state
  • It would be very dangerous to allow consumers to update the 允许消费者更新state of an 一个order
  • A state change would much more likely be triggered by another endpoint (such as 状态更改更有可能由另一个端点触发(例如/orders/{id}/fulfill)

13. Be as specific as possible

13. 尽可能具体

As outlined in the previous section, it’s generally a good idea to be as specific as possible when designing endpoints, naming fields, and deciding which requests and responses to accept. If a PATCH request only accepts two fields (name and description), there is no danger of using it incorrectly and corrupting data.
如上一节所述,在设计端点、命名字段以及决定接受哪些请求和响应时,最好尽可能具体。如果一个 PATCH 请求只接受两个字段(名称和描述) ,那么就不存在错误使用和损坏数据的危险。

14. Use pagination

14. 使用分页

Paginate all requests that return a collection of resources and use the same response structure. Use page_number and page_size (or similar) to control which chunk you want to retrieve.
对所有返回资源集合的请求分页,并使用相同的响应结构。使用页码和页大小(或类似)来控制要检索的块。

15. Allow expanding resources

15. 允许增加资源

Allow consumers to load related data using a query parameter called expand (or similar). This is especially useful to avoid round-trips and load all data that is necessary for a specific action in one go.
允许使用者使用称为 expand (或类似)的查询参数加载相关数据。这对于避免往返和一次性加载特定操作所需的所有数据尤其有用。
  • 思考
  • Hugo博客自动更新强大的模块化计算机
    Loading...