注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

逍遥子 曰:

得失失得 何必患得患失 舍得得舍 不妨不舍不得

 
 
 

日志

 
 

[译]优秀RESTful API的设计原则(二)  

2017-01-10 19:11:20|  分类: 架构设计 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

原文来自:https://codeplanet.io/principles-good-restful-api-design/

端点

端点是URL中用于标识一个特定资源或资源集合的那部分URL片段。[逍遥子笔记:例如:https://api.example.com/v1/zoos中的/zoos]

假如你想构建用于表示多个动物园资源的API,其中,每个动物园都包含很多动物(每个动物只能属于一个动物园),顾员(他们可以在多个动物园工作),并且需要跟踪每个动物的详细信息,那么这些API的端点可能如下所示:[逍遥子笔记:下列URL中红色加粗的后缀就是端点]

l  https://api.example.com/v1/zoos

l  https://api.example.com/v1/animals

l  https://api.example.com/v1/animal_types

l  https://api.example.com/v1/employees

在介绍这些端点的作用时,你需要给出这些端点以及操作它们的HTTP动作。例如下面给出的是刚才所构建动物园API列表的功能,注意,我在每个端点前面都加上了HTTP动作,就像HTTP请求中所使用的那样。

l  GET    /zoos:列出所有的动物园(包括动物园的ID、名称以及简介);

l  POST   /zoos: 创建一个新的动物园;

l  GET    /zoos/ZID:获取一个完整的动物园对象;[逍遥子笔记:这里的动物园对象仅仅指“动物园”,例如动物园ID、名称、介绍等信息,而不包含该动物园中的动物、雇员等下辖类型的信息]

l  PUT    /zoos/ZID:更新一个动物对象(包含全部信息);

l  PATCH  /zoos/ZID:更新一个动物对象(包含部分信息);

l  DELETE /zoos/ZID:          删除一个动物园对象;

l  GET    /zoos/ZID/animals:获取指定动物园的全部动物信息(包括动物的ID和名称);

l  GET    /animals:是指列出所有动物信息;

l  POST  /animals: 创建一个新的动物;

l  GET   /animals/AID: 获取某个动物的对象信息;

l  PUT   /animals/AID:更新一个动物的对象信息(提供对象的全部内容);

l  PATCH  /animals/AID:更新一个动物的对象信息(提供对象的部分内容);

l  GET    /animal_types: 获取所有的动物类型信息;

l  GET    /animal_types/ATID: 获取指定动物类型的类型描述信息;

l  GET    /employees:获取所有的雇员列表;

l  GET    /employees/EID:获取一个指定雇员的信息;

l  GET    /zoos/ZID/employees:获取指定动物园的全部雇员列表;

l  POST   /employees:创建一个新雇员;

l  POST   /zoos/ZID/employees:为指定动物园增加一个雇员;

l  DELETE /zoos/ZID/employees/EID:解雇指定动物园的某个雇员;

上述列表中,ZID表示动物园的IDAID表示动物IDEID表示雇员IDATID表示动物类型ID,在文档中给出关键词及含义是一个非常好的习惯。

在上面的例子中我已简要列出常见APIURL前缀,这种简化方式(省略URL前缀)非常有利于沟通,但是在你的API文档当中,还是要使用全部的URL(例如:GET https://api.example.com/vi/animal_type/ATID)。[逍遥子笔记:在非正式文档中介绍一个API时可以采用端点代替完成URL这种方法:HTTP动作+端点+该端点的功能说明,端点要比完整URL短的多,这样更容易表述,也不影响理解,但是在正式文档中还是要把URL写全]

这里需要注意数据之间关系的展示,尤其是雇员和动物园之间的多对多的关系。你可以通过增加URL的方式来表示更多的数据关系[逍遥子笔记:根据Roy Thomas Fielding对资源的解释,关系也是一种资源,因此,在遇到多对多的数据关系时,可以将数据关系进行拆分,并为每个关系都增加一个URL]。当然,这里并没有一个HTTP操作能表示解除雇员,但是我们可以通过删除指定动物园的雇员的方式来达到同样的效果。[逍遥子笔记:这是对上面“DELETE /zoos/ZID/employees/EID:解雇指定动物园的某个雇员这个条目的解释]

过滤器

当用户请求获取一组对象列表时,你就需要对结果进行过滤并返回一组严格符合用户要求的对象。有时返回结果的数量可能非常大,但是你也不能随意对此进行约束,因为这种服务端的随意约束会造成第三方开发人员的困惑。如果用户请求了一个集合,并对返回结果进行遍历,然后只要前100个对象,那么这里就需要由用户来指明这个限制量。这样用户就不会有这样的疑惑:是他们程序的bug还是接口限制了100条?还是网络只允许传这么大的包?[逍遥子笔记:在IM项目中有个接口让用户拉取自己的历史消息,我们就需要在接口中增加一个参数让用户来确定本次要拉取多少条历史消息,服务器端根据用户传入的限制量来确定返回消息的条数,而不是由服务器来在实现时就确定该接口一次调用只能返回几条]

尽量减少对第三方开发人员的随意约束。[逍遥子笔记:不要在接口中添加默认的约束条件]

非常重要的一点:让第三方开发人员自己指定排序过滤器/返回结果集的约束条件。这么做的最重要原因是:用户能用尽量少的网络消耗尽快获取到结果;第二个重要原因是:用户可能很懒,想让服务端帮他们做好分页和过滤;还有一个对客户端不那么重要,但是对服务端很重要的原因:这么做会降低请求的资源负载。

过滤器通常用于过滤GET请求返回的资源集合,在GET请求中,可以通过URL传递过滤器信息。你可以放心把下面例子中列出的过滤器类型应用到自己的API中:

l  ?limit=10:限制返回给用户的结果集的数量(通常用于分页);

l  ?offset=10:给用户返回一个结果集(通常用于分页);[逍遥子笔记:指定偏移量,从指定位置开始返回结果集,与limit结合使用就可以达到分页效果,第一次从开始获取指定数量的结果,后续都要从上次结果之后开始返回]

l  ?animal_type_id=1:返回符合条件的结果集(类似于数据库的WHERE查询条件:WHERE animal_type_id=1);

l  ?sortby=name&order=asc:将结果集按照指定属性和指定排序方式进行排序(类似于数据的ORDER BY name ASC);

上述部分过滤器与前面介绍的某些URL端点功能重复,例如前面提到的URLGET /zoo/ZID/animals在功能上就与使用过滤器的GET /animals?zoo_id=ZID重复。功能单一的端点对于第三方开发人员来说更容易使用,尤其是他们使用你提供的请求做一些复杂的开发时,更是如此。在API文档中明确写出这些功能重复的请求方式,将会消除第三方开发人员对这些重复功能的困惑,否则他们就会怀疑这些重复功能之间是否有差异!

还有一点,当需要对数据进行过滤或者排序时,你要给第三方开发人员(Consumer)列出哪些属性能用于过滤或排序,我们不希望把任何数据库操作的错误返回给用户。[逍遥子笔记:不要把服务内部的错误或者问题暴露给第三方开发人员]

状态码

充分利用适当的状态码对于设计REST 风格的API来说非常重要,因为HTTP状态码已有标准定义,并且各种网络设备都能读取并识别这些它们。例如,通过配置负载均衡器的参数来避免将请求发往出现50X错误的服务程序[逍遥子笔记:50X表示服务程序内部出错]。这里将列出一些可供你选择使用的HTTP状态码,它们可以成为你设计良好返回码的出发点:

l  200 OK – [GET]

         客户端向服务器请求数据时,服务器找到这些数据并将之返回给客户端(此行为幂等);[逍遥子笔记:GET操作只能获取数据(即只读),不应该对服务器的数据进行任何形式的修改]

l  201 CREATED – [POST/PUT/PATCH]

客户端向服务器发送数据,服务器为这些数据创建一个资源;

l  204 NO CONTENT – [DELETE]

客户端请求服务器删除一个资源时,服务器将该资源删除;[逍遥子笔记:返回码204表示执行成功了,但是没有数据。HTTP RFC2616中对于204返回码的描述为:如果客户端是个代理(例如浏览器),它不应该改变“触发该请求的”页面展示,该返回值主要用于输入行为发生时,虽然新的或更新过的元数据信息被应用于当前页面,但代理(浏览器)不能改变当前的页面展示,原文为:

If the client is a user agent, it SHOULD NOT change its document view from that which caused the request to be sent. This response is primarily intended to allow input for actions to take place without causing a change to the user agent’s active document view, although any new or updated metainformation SHOULD be applied to the document currently in the user agent’s active view.]

l  400 INVALID REQUEST – [POST/PUT/PATCH]

客户端给服务器发送了一个无效的请求,服务器对此不作任何动作(此行为幂等)。

l  404 NOT FOUND – [*]

客户端请求了一个不存在的资源或资源集合,服务端对此不作任何动作(此行为幂等)。

l  500 INTERNAL SERVER ERROR – [*]

服务器内部发生了错误,客户端无法知道请求是否被执行成功了。

状态码范围

1XX的返回码预留给HTTP的底层使用,在你的整个职业生涯中都不会主动发送这种返回码;

2XX的返回码表示请求按照预期执行并成功返回了信息。服务端要尽可能给用户返回这种结果。

3XX的返回码表示请求重定向,大多数API都不会经常使用这种请求(),但是最新的超媒体API会充分使用这些功能。

4XX的返回码主要表示由客户端引起的错误,例如请求参数错误或者访问一个不存在的资源,这些必须为幂等操作,并且不能改变服务器的状态[逍遥子笔记:其实服务器的状态发生了改变就意味着操作不是幂等了]

5XX的返回码主要表示由服务器引起的错误,通常情况下,这些错误都是开发人员([逍遥子笔记:这里应该是服务器程序的开发人员])接触不到的底层函数抛出来,然后传递给用户([逍遥子笔记:这里应该是第三方开发人员])的。用户在收到5XX的返回码时,他们不知道服务器当前的工作状态是否正常,因此,要尽量避免这种情况发生。

返回值文档

当第三方开发人员在发送HTTP请求时,他们需要事先了解这些请求的返回值信息,下述列表就是一些REST风格API以及它们对应的返回值信息:[逍遥子笔记:与前面的介绍URL的功能类似,只是这里将最后面的URL描述换成返回值描述,这里依然采用三段式描述法:HTTP请求动作+端点+端点对应的返回值描述信息]

l  GET /collection:返回一个资源对象的列表;

l  GET /collection/resource: 返回一个资源对象;

l  POST /collection:返回新创建的资源对象

l  PUT /collection/resource:返回一个完整的资源对象;

l  PATCH /collection/resource:返回一个完整的资源对象;[逍遥子笔记:虽然请求中只带了部分资源对象的内容,但是返回的内容却是整个资源对象]

l  DELETE /collection/resource:返回一个空文档;

需要注意的是:当用户创建一个资源时,他们通常并不想知道新创建资源的ID(也不想知道其他属性,例如修改或创建的时间戳)[逍遥子笔记:这一点略有疑惑,这种设计思路应该也是区分场合的,如果资源很复杂,本文介绍的这种思路或许可行,如果资源原本就很简单,例如我们以前设计的发送消息接口,就直接给用户返回所发送消息的ID]。这些新增的属性信息可通过后续请求获得,当然也可以通过初始化POST请求来返回。


  评论这张
 
阅读(198)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017