以下是设计REST API的提示,建议
引用自:API design
面向资源(resources)编程
1.了解应用于REST的HTTP的基础知识
如果您要构建设计良好的REST API,您最好了解HTTP协议的基础知识。我真的相信这将有助于您做出更好的设计决策。
我发现MDN Web文档上的HTTP概述对此非常好。但是,就REST API设计而言,这里是TL; 应用于REST的HTTP的 DR :
HTTP有动词(或方法):GET,POST,PUT,PATCH和DELETE是最常见的。
REST是面向资源的,资源由URI表示:/newspapers/。
一个端点是一个动词和URI,例如组合GET: /articles/。
端点可以解释为对资源的操作。例如,POST: /articles/可能表示“创建新文章”。
在高层次,动词映射到CRUD操作:GET方法Read,POST手段Create,PUT和PATCH平均Update,并DELETE指…好,Delete。
响应的状态由其状态代码指定:1xx有关信息,2xx成功,3xx重定向,4xx客户端错误和5xx服务器错误的信息。
当然,您可以使用HTTP协议为REST API设计提供的任何内容,但这些是我认为您需要记住的基本内容。
2.不要返回纯文本
虽然它不是由REST架构风格强加的,但大多数REST API都使用JSON作为数据格式。
但是,返回包含JSON格式字符串的主体是不够的。你也需要指定Content-Type标题!必须将其设置为值application/json。
这对于程序化客户端(例如,某人或通过requestsPython中的库与API交互的服务)尤为重要- 其中一些客户端依赖此标头来正确解码响应。
专业提示:您可以Content-Type使用Firefox轻松验证响应。它内置漂亮的显示响应Content-Type: application/json。?
在Firefox中,“Content-Type:text / plain”看起来很简单。
在Firefox中,“Content-Type:text / plain”看起来很简单。
检测到“Content-Type:application / json”! 看看它有多漂亮和实用。 ?
检测到“Content-Type:application / json”!看看它有多漂亮和实用。?
3.避免在URI中使用动词
如果你已经理解了基础知识,你现在就知道将谓词放在URI中并不是RESTful。
这是因为HTTP谓词应该足以描述对资源执行的操作。
假设您要提供一个端点来生成和检索文章的横幅图像。我会注意到:paramURI参数的占位符(如ID或slug)。您可能想要创建此端点:
GET: /articles/:slug/generateBanner/
但是这种GET方法在语义上足以说我们正在检索(“GETting”)横幅。所以,让我们使用:
GET: /articles/:slug/banner/
同样,对于创建新文章的端点:
Don’t
POST: /articles/createNewArticle/
Do
POST: /articles/
HTTP动词所有的东西!
4.使用复数资源名词
可能很难决定是否应该使用复数或单数形式的资源名词。
你应该使用/article/:id/(单数)还是/articles/:id/(复数)?
我建议使用复数形式。
为什么?因为它非常适合所有类型的端点。
我同意这GET /article/2/很好,但是怎么样GET /article/?我们是在系统中获得唯一的文章,还是全部?
为了防止这种歧义,让我们保持一致(生活建议!)并在任何地方使用复数:
GET: /articles/2/
POST: /articles/
…
5.在响应正文中返回错误详细信息
当API服务器处理错误时,方便(并建议!)返回JSON正文中的错误详细信息以帮助用户进行调试。如果您包含哪些字段受错误影响,请特别荣幸!
{
“error”: “Invalid payoad.”,
“detail”: {
“surname”: “This field is required.”
}
}
6.注意状态代码
这个非常重要。如果您需要记住本文中的一件事,可能就是这样:
您的API可能做的最糟糕的事情是返回带有200 OK状态代码的错误响应。
这只是糟糕的语义。而是返回一个正确描述错误类型的有意义的状态代码。
不过,你可能会想,“但是我按照你的推荐在响应体中发送错误细节,那么这有什么问题?”
我来告诉你一个故事。
我曾经不得不使用200 OK为每个响应返回的API,并通过status字段指示请求是否成功:
{
“status”: “success”,
“data”: {}
}
因此即使状态如此200 OK,我也无法确定它是否能够处理我的请求。
实际上,API可以返回响应,例如:
HTTP/1.1 200 OK
Content-Type: text/html
{
“status”: “failure”,
“data”: {
“error”: “Expected at least two items in list.”
}
}
(是的 - 它还返回了HTML内容,因为为什么不呢?)
因此,我必须检查状态代码和ad-hoc status字段,以确保在我阅读之前一切正常data。
这种设计是一个真正的禁忌,因为它打破了API与其用户之间的信任。您会担心API会对您撒谎。
所有这些都非常非RESTful。你应该怎么做?
使用状态代码,仅使用响应正文提供错误详细信息。
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
“error”: “Expected at least two items in list.”
}
7.始终使用状态代码
一旦你掌握了状态代码,你应该努力使用它们consistenly。
例如,如果选择POST端点返回201 Created某个位置,请为每个POST端点使用相同的状态代码。
为什么?因为用户不必担心哪个端点将在哪种情况下返回哪个状态代码。
所以要保持一致,如果你偏离惯例,请将它记录在一个带有大标志的地方。
一般来说,我坚持以下几点:
GET: 200 OK
POST: 201 Created
PUT: 200 OK
PATCH: 200 OK
DELETE: 204 No Content
8.不要嵌套资源
REST API处理资源,并且检索资源的列表或单个实例是串通的。但是当你处理相关资源时会发生什么?
假设我们要检索特定作者的文章列表 - 具有的文章id=12。基本上有两种选择。
第一种选择是将资源嵌套在articles资源下authors,例如:
GET: /authors/12/articles/
有些人推荐它,因为它确实代表了作者和他们的文章之间的一对多关系。
但是,目前尚不清楚您要求的资源类型。是作者吗?是文章吗?
同样扁平比嵌套的好,所以必须有一个更好的办法…而且有!
我的建议是使用查询字符串articles直接过滤资源:
GET: /articles/?author_id=12
这显然意味着:“获取作者#12的所有文章”,对吧??
9.优雅地处理拖尾斜线
URI是否应该具有尾随/并不是真正的争论。只需选择一种方式或另一种方式(即带有或不带有斜杠),坚持使用它并优雅地重定向客户端,如果他们使用错误的约定。
(我必须说我不止一次自己犯了这个罪。?)
讲故事的时间!有一天,当我将REST API集成到项目中时,我不断收到500 Internal Error每一个电话。我使用的端点看起来像这样:
POST: /entities
我很生气,无法弄清楚我做错了什么。
最后,事实证明服务器失败了,因为我错过了一个尾随斜线!所以我开始使用
POST: /entities/
之后一切都很顺利。?♂️
该API未修复,但希望您可以为您的用户阻止此类问题。
专业提示:大多数Web框架都可以选择优雅地重定向到URL的跟踪或未跟踪版本。找到该选项并激活它。
10.使用查询字符串进行过滤和分页
很多时候,简单的端点不足以满足复杂的业务案例。
您的用户可能希望检索满足特定条件的项目,或者一次少量检索这些项目以提高性能。
这正是为过滤和分页所做的。
通过过滤,用户可以指定返回的项目应具有的属性。
分页允许用户检索数据集的分数。最简单的分页是页码分页,由a page和a 确定page_size。
现在的问题是:如何在RESTful API中加入这些功能?
我的回答是:使用查询字符串。
我会说为什么你应该使用查询字符串进行分页很明显。它看起来像这样:
GET: /articles/?page=1&page_size=10
但过滤可能不那么明显。首先,您可能会想到这样做以仅检索已发布文章的列表:
GET: /articles/published/
设计问题:published不是资源!相反,它是您正在检索的数据的特征。这种事情应该在查询字符串中。
所以最后,用户可以检索“包含20个项目的已发表文章的第二页”,如下所示:
GET: /articles/?published=true&page=2&page_size=20
非常明确,不是吗?
11.学习之间的区别401 Unauthorized和403 Forbidden
当处理API中的安全性错误时,很容易混淆错误是否与身份验证或授权(也称为权限)相关 - 一直发生在我身上。
这是我的作弊表,根据情况了解我正在处理的事情:
用户是否未提供身份验证凭据?他们无效吗?? 401 Unauthorized。
用户是否经过了正确的身份验证,但他们没有访问资源所需的权限?? 403 Forbidden。
12.善用 202 Accepted
我觉得202 Accepted这是一个非常方便的替代品201 Created。它基本上意味着:
我,服务器,了解你的要求。我还没有创建资源,但这很好。
我发现有两种情况202 Accepted特别适合:
如果资源将作为未来处理的结果创建 - 例如在作业完成之后。
如果资源已经以某种方式存在,但这不应该被解释为错误。
13.使用专用于REST API的Web框架
作为最后一个最佳实践,让我们讨论这个问题:您如何在API中实际实现最佳实践?
通常,您希望创建一个快速API,以便一些服务可以相互交互。
Python开发人员会抓住Flask,JS开发人员会抓住Express,他们会实现一些简单的路由来处理HTTP请求。
这种方法的问题在于,通常,框架不是针对构建REST API服务器的。
例如,Flask和Express都是两个非常通用的框架,但它们并非专门用于帮助您构建REST API。
因此,您必须采取额外步骤来实施API中的最佳实践。大多数情况下,懒惰或缺乏时间意味着您不会付出努力 - 并为您的用户留下一个古怪的API。
解决方案很简单:使用正确的工具完成工作。