RESTful API设计最佳实践(转译)

发布资源的URL改如何设计?名词使用复数或者单数?对同一个资源,改用多少URL来描述发布?对创建一个资源应该用怎样的HTTP方法(POST,PUT,GET,DELETE)描述?该如何处置可选参数?最好的描述分页和版本的方法又是什么?在设计RESTful API的时候,因为存在很多种可能性,故在设计的时候需要十分的谨慎小心。在这篇文章中,一起来看看设计RESTful API的最佳实践。

每个资源,使用两个URL

一个用来表示集合,另一个用来表示特定的某个元素。

/employees #资源集合
/employees/56 #资源特定元素

用名词替代动词表示URL

以下这些写法,只会让URL看起来完全没有经过精心设计:

/getAllEmployees
/getAllExternalEmployees
/createEmployee
/updateEmployee

作为替代方案,参考以下设计。

使用HTTP方法来操作URL

GET /employees
GET /employees?state=external
POST /employees
PUT /employees/56

URL用来指定需要操作的资源,HTTP方法用来指定对资源进行何种操作。通过使用HTTP的方法,GET,POST,PUT,DELETE,可以用来表示CRUD函数功能(Create,Read,Update,Delete)。

  • Read:使用GET方法读取资源。GET请求不会改变资源状态,无任何副作用,并且是幂等的。GET方法是包含只读语义。因此,可以缓存响应结果。
  • Create:使用POST方法创建新的资源。
  • Update:使用PUT方法更新已存在的资源。
  • Delete:使用DELETE方法删除已存在的资源。

通过2个URL和4个HTTP方法,可以等价于一系列方法操作集合,如下所示:

/ POST(Create) GET(Read) PUT(Update) DELETE(Delete)
/employees 创建新雇员 获取所有雇员 批量更新雇员 删除所有雇员
/employees/56 ... 获取编号56雇员详情 更新编号56雇员详情 删除编号56雇员

在表示集合URL上,使用POST来创建新的资源

对于一个主从式交互过程,创建一个资源该如何展示?


在collection-URL上,使用POST方法来创建资源
  1. 客户端通过collection-URL /employees发送POST方法请求,请求消息体包含新资源“Albert Stark”的相关属性。
  2. 服务端为新的雇员创建ID,创建雇员相关信息并向客户端发送回响应。响应体包含访问新创建资源对应的URL路径,/employees/21。

在表示特定袁术URL上,使用PUT来表示更新资源

使用PUT方法,更新已存在的资源

客户端使用PUT方法,通过URL路径 /employees/21 发送请求。HTTP消息体包含更新的属性信息(ID编号21雇员的新名字)。
服务端更新 ID编号21雇员的新名字,并通过返回HTTP状态码200,确认此次更新成功。

坚持使用复数名词

相比于如下RESTful URL:

/employee
/employee/21

如下RESTful URL更为合适:

/employees
/employees/21

事实上,这只能算一种尝试。但复数形式更加的普遍。此外,这更加直观。尤其是在使用collection-URL,用来表示GET /employees?state=external POST /employees PUT /employees/56,但最重要的是,避免混合使用复数和单数形式,容易让人困惑并且易于出错。

使用“?”来拼接可选或者复杂的参数

不要使用如下URL表示方法:

GET /employees
GET /externalEmployees
GET /internalEmployees
GET /internalAndSeniorEmployees

保持URL简单和URL集合最小化。使用一个基础的URL代表一个资源,将复杂的、可选的参数迁移到URL后作为字符串参数。如下:

GET /employees?state=internal&maturity=senior

使用HTTP状态码

RESTful Web服务应该通过适当的状态码返回给客户端的请求。

  • 2xx – 成功 – 操作成功
  • 4xx – 客户端错误 – 客户端操作错误 (比如客户端发送非法请求或者本身身份并未认证)
  • 5xx – 服务端错误 – 后台出现错误 (比如后台无法处理请求)

参考维基上HTTP状态码 。不论如何,如果利用所有的状态码,势必会让API的调用者感到困惑。所有将状态码的使用保持在一个较小的集合内,通常如下所示:

2XX:Success 3XX:Redirect 4XX:Client Error 5XX:Server Error
200 OK 301 Moved Permanently 400 Bad Request 500 Internal Server Error
201 Created 304 Not Modified 401 Unauthorized .
. . 403 Forbidden .
. . 404 Not Found .

提供有效的错误码或错误提示

除了要有适当的状态码,还需要在HTTP响应消息体中提供有用并稍显啰嗦的关于错误信息的描述。
请求:

GET /employees?state=super
响应:
// 400 Bad Request
{
"message": "You submitted an invalid state. Valid state values are 'internal' or 'external'",
"errorCode": 352,
"additionalInformation" : "http://www.domain.com/rest/errorcode/352"
}

使用驼峰规则表示属性名称

对属性定义使用驼峰规则。

{ "yearOfBirth": 1982 }

不用使用下划线(year_of_birth) 或者全大写字母开头。通常来说,客户端通常使用JavaScript来解析RESTful web服务返回的响应信息。常见于,客户端通常将JSON转换成JavaScript对象,比如:

var person=JSON.parse(response)

因此,使用JavaScript的约定是的JavaScript更容易阅读和习惯性表达。
比较如下表达方式:

person.year_of_birth //violates JavaScript convention
person.YearOfBirth //suggests constructor method

person.yearOfBirth //nice!

总是强制在URL中包含版本号

在发布RESTful API的时候,需要带上版本号。强制将版本号发布到URL上,这样API能更加容易发生演进。将版本号加入API,调用的客户端可以根据他们自身情况有条理的对现有API迁移和过度。客户端不会因为突然出现新的行为发生改变的API有太多的困惑。
使用“v”开头,并跟上数字版本号作为标记。

/v1/employees

不需要使用小版本号(“v1.2”),因为API的改变不会非常频繁。相比较于API和文档语义,作为API的实现可以经常性的改进。

提供分页查询支持

一次将所有资源从数据库返回绝对不是一个好的注意。所以,需要提供分页机制。一般使用参数 offset 和 limit,向数据库提供过滤。

/employees?offset=30&limit=15 #returns the employees 30 to 45

需要对客户端忽略的参数提供默认值,防止返回全部的查询资源结果。通常如 offset=0 和 limit=10 是比较适当的。假如后台检索十分昂贵,则需要增加更多的参数限制。

/employees #returns the employees 0 to 10

此外,在使用分页时,需要返回客户端资源总数,
请求:

GET /employees

响应:

{
"offset": 0,
"limit": 10,
"total": 3465,
"employees": [
//...
]
}

对非资源型响应使用动词表达

有时候,一个API的响应并不包括资源,如下:

GET /translate?from=de_DE&to=en_US&text=Hallo
GET /calculate?para2=23&para2=432

这种情况下,API不返回任何资源。相反,需要执行相关操作和返回结果。因为,在URL设计上,需要使用动词用来替代名词,用以区分非资源响应和资源相关的响应。

考虑特定资源和跨域资源查找

提供对资源指定类型的查找是方便的。只要使用一致的collection-URL,然后加上查找关键词即可。

GET /employees?query=Paul
加入需要提供一个所有资源的查询,需要一个稍微不同的方法。想想上面提到的关于非资源类URL设计,使用动词替代名称。因此,设计的查询URL可参考如下:
GET /search?query=Paul //returns employees, customers, suppliers etc.

通过API,提供其他链接

理想上来说,可以不需要客户端动态构造URL,来访问REST API,可以考虑下面一个例子。
一个客户端想访问一个雇员的工资清单。此外,他还知道通过增加关键字“salaryStatements”到雇员URL(如:/employees/21/salaryStatements)访问工资清单。这种字符串串联非常容易出错,并非很难维护。但假如改变工资清单的访问方式(如使用新的“salary-statements”或者“paySlips”),则所有客户端都无法访问。
最好的方式是在响应中提供相关链接,比如:
请求:

GET /employees/

响应:

//...
{
"id":1,
"name":"Paul",
"links": [
{
"rel": "salary",
"href": "/employees/1/salaryStatements"
}
]
},
//...

这样,假如客户端依赖返回的链接获取工资清单,即使改变API,也不会出现无法访问的问题,因为客户端总是可以获取一个合法的URL(即使一直在改变URL)。另外一个好处是,服务端的API变的更加具有自我描述性,需要更少的文档。
当在分页使用中,可以提供下一页或者上一页的链接。仅仅提供链接和相关的 offset 和 limit 参数。

GET /employees?offset=20&limit=10

{
"offset": 20,
"limit": 10,
"total": 3465,
"employees": [
//...
],
"links": [
{
"rel": "nextPage",
"href": "/employees?offset=30&limit=10"
},
{
"rel": "previousPage",
"href": "/employees?offset=10&limit=10"
}
]
}

以上文章的 原文地址 ,可以看原文如何写。

PS:第一次翻译,翻译的很局促,大家见谅。有表达不合适的,大家提,我修改。

这是我博客新地址,欢迎大家一起交流 https://lxzchen.github.io

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容