好的restful API设计很难!API代表了你和使用你的数据的人之间的契约。如果打破这个契约将会带来很多愤怒的邮件,导致大量使用移动app的人因不能使用很悲伤。而文档仅仅是成功的一半,并且很难找到喜欢写文档的程序员。
构建一个API是增加你的服务的价值你所能做的最重要的事情。拥有API,你的服务/核心应用有潜力成为别的服务成长依赖的平台。看看现在一些大的科技公司:Facebook,twitter,Google,github,amazon,netflix…如果他们不通过API开放他们的数据,他们都不会变成如今这么壮大。事实上,整个行业存在的唯一目的就是消费上述平台提供的数据。

你的API越容易使用,就越多的人使用它

这篇文章的原则,如果在设计你的API时密切关注,将会确保你的API的使用方能够理解正在进行什么,并能彻底地减少你收到的疑惑和/或抱怨的邮件。我已经把所有相关的组织成了话题,不需要按顺序来读。

术语

这里是我讲在通篇章节里使用的重要的术语:
资源:一个对象的单个实例。比如:一个动物。
集合:一个同类型对象的集合。比如:一群动物。
HTTP:网络传输协议
Consumer(使用方):能发送http请求的客户端应用程序。
第三方开发者:不是你项目组成员但是想使用你的数据的开发者。
Server(服务):一个HTTP 服务/应用能通过网络被Consumer访问。
Endpoint:一个Server上能代表一个Resource或者全部Collection的API URL
幂等性:无副作用,可多次发生而无副作用。
URL段:URL中由斜杠(“/”)分隔的信息段。

数据设计和抽象

规划你的API长什么样要比你想象的早;首先你需要决定你的数据如何设计和你的核心服务/应用如何工作。如果你在做“API First Development”这样容易些。如果你要给一个已经存在的工程附加API,你可能需要提供更多的抽象。
偶尔,一个集合可以代表一个数据库表,一个资源可以代表该表中的一行。然而,这不是常用的case。实际上,你的API应该尽可能和你的数据和业务逻辑越抽象越好。不让任何复杂的应用数据使第三方开发者受不了是非常重要的,如果你做了他们就不会想用你的API了。
也有很多你服务的部分你绝对不想通过API暴露出来。一个常见的例子就是很多API不会允许第三方来创建用户。

动词

你肯定知道GET和POST请求。当你的浏览器访问不同web页面时有这两种常用的请求。POST如此流行以至于它已经侵入了常用的语言,那些不知道任何关于网络如何运转的人也知道他们可以“post”一些东西到朋友的Facebook“墙”。
有4个半非常重要的HTTP动词你需要去了解。我说“半”,是因为PATCH动词和PUT动词非常相近,这两个经常被很多开发者结合在一起。本节讲动词,下一节讲和他们关联的数据库调用(我敢肯定大部分人读到这个相对于设计API会了解更多关于写数据库)。
GET(SELECT):从服务或者一系列资源中查找一个特定的资源。
POST (CREATE):在服务上创建一个新资源。
PUT (UPDATE):更新服务上的一个资源,提供整个资源。
PATCH (UPDATE): 更新服务上的资源,仅仅提供改变的属性。
DELETE (DELETE):从服务移除一个资源。
下面是两个不常见的HTTP动词:
HEAD - 查询一个资源的元数据(meta data),诸如数据的hash值或者最后更新时间。
OPTIONS - 查询使用方可以在资源上操作的信息。
一个好的RESTful API会利用这四个半HTTP动词来允许第三方和它提供的数据交互,并从不在URL段里包含任何动作/动词。
.一般,GET请求可以被浏缓存(经常如此!)。浏览器,会缓存GET请求(依赖于缓存头),会提示用户,如果他们试图POST第二次。HEAD请求基本上是一个不带返回结果体的GET,也能被缓存。

版本控制

无论你再构建什么,无论你提前计划多少,你核心的应用都一直在变,你的数据管理会变,属性会不断地从你的资源里添加或删除。这就是软件开发的工作方式,并且格外真实,如果你的项目还活着并被很多人使用(这就像你构建API的例子)。
记住API是服务和使用方之间的契约。如果你改变了服务API并且这些改变打破向后兼容性,你会让使用方事情弄糟,他们也会因此恨你。做的足够,他们就会离开。为保证你的应用发展并且保证你的使用方满意,你需要偶尔引入新版本API而同时保证旧版本的可用。
顺便说一下,如果你仅仅给你的API增加新特性,诸如一个资源的新属性(不是必须的并且没有这些属性资源也能使用),或者你在增加一个新的Endpoint,你不需要升级你的API版本号,因为这些改变不需要向后兼容。当然,你需要更新你的API文档(你的契约)。
经过一段时间,你可以deprecate(不赞成)API的老版本,deprecate一个特性不表示关掉它或者降低它的质量,只是告诉使用方你的老版本API会在一个特定日期移除,他们需要更新到更新的版本。
一个好的restful API设计会在URL中跟踪版本。其他大部分通用解决方案是把版本号放在请求头里,但经过和很多第三方开发者一起合作后,我可以告诉你加到请求头里没有加到URL段里容易。

分析

跟踪被使用方使用的API的版本/Endpoint。这就像每次创建请求给数据库增加一个整型一样容易。有很多理由支持跟踪API分析是一个好主意,比如,最被常用的API调用应该更有效率。
为了构建一个第三方开发者喜爱的API,最重要的事情就是当你deprecate你的API的一个版本时,你可以通过被不建议使用的API特性和开发者接触。这是最完美的方式来提醒他们在你下掉久API版本前升级。
通知第三方开发者的方式可以是通知,比如,没调用不建议使用特征10000次就发邮件给开发者。

API根URL

不管你信不信,你的API的根位置很重。当一个开发者()使用你的API继承一个老的项目,并需要构建新特性,他们可能一点也不知道你的服务。也许他们所知道的仅仅是使用方调用的一串URL。你的API根入口尽可能保持简单是非常重要的,因为一个长而且复杂的URL将让人望而生畏并且让开发者远离。
下面是两个通用URL根:
https://example.org/api/v1/*
https://api.example.com/v1/*
如果你的应用很庞大,或者你促使它变得庞大,把API放进它自己的子域(比如api)是一个好的选择。这能为以后留下更灵活的扩展。
如果你预估你的API永远不会变得那么大,或者你想要一个更简单的应用安装(比如:你想从相同的框架托管网站和API),把你的API放在域名根(比如/api/)的URL段下面也可以。
让你的API根用内容是一个好主意,比如访问GitHub的API根返回一串Endpoint。就个人来说,我是一个让根URL给迷惑的开发者一些有用的信息的想法的粉丝,比如,如何获取API的开发文档。
另外,注意HTTPS前缀,作为一个好的restful API,你必须让你的API支持HTTPS。

ENDPOINTS

一个Endpoint是你API中一个指向特定资源或者资源集合的URL。
如果你在构建一个虚构的API来表示几个不同的动物园,每一个动物园有很多动物(一个动物只属于一个确定的动物园),员工(工作在多个动物园),你跟踪每个动物的种类,你会得到下面Endpoint:
https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/animal_types
https://api.example.com/v1/employees
当查阅每个Endpoint可以做什么时,你会想要列出合法的HTTP动词和Endpoint组合关系。比如,下面是一个别人可能用我们虚构的API执行的长长的列表。注意我已经在每个Endpoint前面加上HTTP动词,因为这是在HTTP请求头里同样的记号。

GET /zoos:罗列出所有的动物园 (ID和名字, 没有太多的细节)
POST /zoos: 创建一个新的动物园
GET /zoos/ZID: 获取一个完整的动物园对象
PUT /zoos/ZID: 更新一个动物园 (整个对象)
PATCH /zoos/ZID: 更新一个动物园 (部分对象)
DELETE /zoos/ZID: 删除一个动物园
GET /zoos/ZID/animals: 获取动物列表 (ID和名字).
GET /animals: 列出所有的动物 (ID和名字).
POST /animals: 创建一个新动物
GET /animals/AID: 获取一个动物对象
PUT /animals/AID: 更新一个动物 (整个对象)
PATCH /animals/AID: 更新一个动物 (部分对象)
GET /animal_types: 获取一个所用动物类型的列表 (ID和名字)
GET /animal_types/ATID: 获取一个整个动物类型对象
GET /employees: 获取整个员工列表
GET /employees/EID: 获取一个特定员工
GET /zoos/ZID/employees: 获取在同一个动物园工作的员工列表(ID和名字)
POST /employees: 创建一个员工
POST /zoos/ZID/employees: 在特定的动物园雇佣一个员工
DELETE /zoos/ZID/employees/EID: 从一个特定动物园解雇一个员工
在上面列表中,ZID代表动物园ID,AID代表动物ID,EID代表员工ID,ATID代表动物类型ID。不管你选择何种约定都在文档中有一个key是一个好主意。
为了方便我在上面的例子中省去了公共API URL前缀,虽然这能在语境中找到,但是在你的实际API文档中,你必须一直给每个Endpoint列出完整的URL(比如http://api.example.com/v1/animal_type/ATID)。
注意如果显示数据之间的关系,特别是员工和动物园之间的多对多关系。通过增加一个额外的URL段,别人可以完成更多的交互。当然没有HTTP动词来表示“解雇”一个员工,但是通过对一个动物园的一个员工执行DELETE操作,可以获取同样的效果。

过滤

当一个使用者请求一串对象,你返回给他们一串每个都符合请求条件的对象是很重要的。这个列表可能很大,但是,你不要做任何随心所欲的限制很重要。正是这些随心所欲的限制让第三个开发者很难理解怎么回事。如果他们请求一个特定的集合,并且遍历结果,他们从没看过超过100项,现在他们的工作就是找出这个限制在哪里。是他们的ORM bug限制了100项?还是网络切割了最大包?

把强加给第三方开发者随意的限制减到最少

然而,你确实提供给使用方去指定一些对结果的某种过滤/限制的能力是很重要的。最重要的原因是这样网络活动可以最小,使用方可以尽可能快地拿到结果。第二最重要的原因是使用方可能很懒,如果服务能够为他们提供过滤和分页会更好。没那么重要的原因(从使用方观点来说),对服务也是一个好处,就是请求将减少消耗大的资源。
过滤主要在对资源集合执行GET时候很有用。因为这些事GET请求,过滤信息应该通过URL传过去。下面是一些你可能想加到你的API上的一些过滤类型:
?limit=10: 减少返回给使用方的结果个数(用于分页)
?offset=10: 发送信息的集合给使用方(用于分页)
?animal_type_id=1: 过滤匹配下面条件的记录(WHERE animal_type_id = 1)
?sortby=name&order=asc: 按指定的属性排序结果(ORDER BY name ASC)
一些过滤可能在Endpoint URL上是多余的。比如我之前提到的GET /zoo/ZID/animals。这和GET /animals?zoo_id=ZID是一样的。专门提供给使用方的Endpoint会让他们过的轻松,特别是在那些你预料到他们会创建很多的请求上。在文档中,标注这些溶于信息让第三方开发者不会留下疑惑是否有差异存在。
另外,不用说,无论何时你执行数据的过滤或者排序时,确保你列出了使用方能够过滤和排序的列。我们不想发送给使用方有任何的数据库错误。

状态码

作为一个restful API,使用合适的HTTP状态码是一件很重要的事;毕竟这是一个标准!各种网络设备都可以识别这些状态码,比如,负载均衡可以配置避免发送请求给返回大量50x错误的web服务。有大量HTTP状态码可以选择,但是下面这些会是一个好的起点:
200 OK – [GET]
使用方从服务请求数据,并且服务找到了数据(幂等的)
201 CREATED – [POST/PUT/PATCH]
使用方发送给服务数据并且服务创建了资源
204 NO CONTENT – [DELETE]
使用方请求服务删除资源,并且服务删除成功。
400 INVALID REQUEST – [POST/PUT/PATCH]
使用方发送给服务错误的数据,并且服务没有进行任何操作(幂等的)。
404 NOT FOUND – [*]
使用方请求一个不存在的资源或者集合,且服务器什么也没做(幂等的)
500 INTERNAL SERVER ERROR – [*]
服务器遇到错误,使用方不知道请求是否成功。

状态码范围

1xx范围为低级别http状态预留,你会很有可能你的整个职业生涯都没有手动发送其中一个状态码。
2xx范围为一切按计划执行的成功的消息预留。尽力确保你的服务发送尽可能多的这些状态码给使用方。
3xx范围为流量重定向预留。大部分API不怎么使用这些请求(远没有做SEO的人使用这些多;),然而,新的超媒体风格的API会更多的使用这些。
4xx范围为响应使用方造成的错误预留,比如,他们提供坏的数据或者请求不存在的东西。这些请求必须是幂等性的,并且不应该改变服务的状态。
5xx范围为当服务出错时响应预留。有很多时候这些错误被低级别函数甚至不是开发者写的函数抛出,为了确保使用方获取某种返回结果。使用方在收到5xx的回复时可能不知道服务的状态,因此这些应该避免。

预期返回文档

当使用不同的HTTP动词在服务Endpoint上执行动作时,使用方需要在返回值中获取某种信息。下列是非常典型的restful API:

GET /collection: 返回一系列(组)资源对象
GET /collection/resource: 返回一个资源对象
POST /collection: 返回最新创建的资源对象
PUT /collection/resource: 返回完整的资源对象
PATCH /collection/resource: 返回完整的资源对象(译注:应该是部分)
DELETE /collection/resource: 返回一个空文档
注意当使用方创建一个资源,他们经常不知道资源被创建时的ID(或者别的属性诸如创建和修改时间戳,如果可以)。这些额外的属性会在后来的请求中返回,当然也作为最初的POST的返回值。

身份验证

大部分时间一个服务会想准确地知道谁在在哪个请求。的确,一些API提供Endpoint来被普通(匿名的)大众使用,但是大部分时间工作都是被一个用户来执行。
OAuth 2.0提供很多方式来做这个。对于每个请求,你可以确定你知道是哪个使用方的请求,他们以User来代表请求,并提供一个(通常)标准方式来让使用方使访问过期或者允许User去撤销访问,都不需要第三方使用者知道User的登陆认证信息。
也有OAuth 1.0和xAuth来实现同样的功能。不管你选择哪种方法,确保它是一些普遍的,并且有很好的文档信息,为使用方可能用到的语言/平台而写的不同库。
我很真诚地告诉你OAuth 1.0a,尽管它是一个选项中最安全的,但是实现起来确实很蛋疼的。我很惊讶于那些由于库文件已经不存在了而不得不实现他们自己的库的第三方开发者的数量,我已经花费了很多时间来调试神秘的“无效签名”错误来建议你另选一个。

内容类型

当前,API最“令人激动”的是通过restful接口提供JSON数据。这包括Facebook,Twitter,GitHub,凡是您想得到的。XML似乎已经输掉战争很久了(除了在大型企业环境)。SOAP,谢天谢地,已经死透了。我们的确看不到太多API提供HTML来使用(除非,这是,你在构建一个刮板!)
使用流行的语言和框架的开发者可能非常喜欢解析任何你返回给他们的合法的数据格式。如果你在使用一个不同的序列化构建一个常用的response对象,你甚至可以非常轻松地提供任何上述数据格式(不包括SOAP)。可是需要关注的是,你需要在返回数据事使用Accept(可接受的)头。
一些API创造者推荐给URL(在Endpoint后)增加一个.json,.xml或者.html文件扩展来指定返回的内容类型,然而我不是这样做的粉丝。我真的很喜欢可接受的头(被内嵌到HTTP规范里),并感觉这是最恰当的事情。

超媒体API

超媒体API和restfulAPI设计风格很像。他们的确是非常惊艳的概念,即“回到根源HTTP和HTML如何工作的”。
当处理非超媒体restful API时,URL Endpoint是服务和使用方之间的契约的一部分,这些Endpoint必须提前被使用方知道,并且改变他们意味着使用方不能再按预期地和服务交流。这,你可以想象,是很大的限制。
现在,API使用方当然不仅仅是在网络上做HTTP请求的用户代理。远非如此,人和他们的web浏览器,是做HTTP请求的最常用的用户代理。然而,人不会不会禁锢于这种预先定义restful API的Endpoint URL契约上。什么是人这么特别?当然,他们能够读内容,点击看起来很有趣的链接,一般会探索一个网站,表达内容去到他们想去的地方。如果一个URL改变,一个人不会受影响(除非,这是他们收藏一个网页,通过这种方式他们到达首页并且发现一个新路线到他们喜欢的数据。)
超媒体API概念看起来和人想要的方式一样。请求API的根返回一串可能指向每个信息的集合,并且以使用方能够理解的方式描述每个集合的URL。提供每个资源的ID不重要(或者需要),只要提供了URL。
在超媒体API使用方爬链接并收集信息时,URL一直在返回值中最新的,并且不需要作为契约的一部分被事前知道。如果一个URL之前被缓存,后来返回一个404,使用方可以简单地返回到根并再次找到内容。
当检索集合内一串资源时,一个包含单独的资源的完整URL被返回。当执行POST/PATCH/PUT时,response可以是一个3xx重定向到完整的资源。
JSON没有明确给出我们需要指定哪个属性是URL的语义,或URL如何关联到当前文档。HTML,正如您可能猜想得那样,没有提供整个信息。我们可能看到我们的API转一圈回到原地又返回使用HTML。考虑到我们已经随着CSS走了多远,有一天我们甚至会看到API和网址去使用完全一致的URL和内容变得司空见惯。

文档

老实说,如果你不100%遵照这篇指南中的标准,你的API不一定变得很可怕。但是,如果你不为你的API添加文档,没人会打算使用它,它将是一个可怕的API。
让你的文档可被未身份验证的开发者找到。
不要使用自动文档生成器,或者如果你这样做了,至少你要确保你已经修改了并让它像样。
不要截断示例请求和返回体;把所有的东西展示出来。在你的文档中使用语法高亮。
为每一个Endpoint记下期待的返回(response)码和可能的错误信息,以及什么会造成这些错误信息。
如果你有多余的时间,构建一个开发者API终端让开发者能够直接实验你的API。这没有你想象的难并且开发者(不管是内部的还是第三方的)都会因此爱上你。
确保你的文档能打印;CSS是强大的东西;当文档打印时不要害怕隐藏导航栏。即使没有人会打印物理拷贝,你会惊讶于很多开发者喜欢打印成PDF来线下阅读。

勘误表:原始HTTP包

由于我们做的每件事都是基于HTTP,我打算给你展示一个HTTP包的剖析。我常常惊讶于如此多的人不知道这些东西长的什么样!当使用方发送一个请求给服务器,他们提供一组键值对,称作头(Header),随着两个换行符,最后是请求体。这都是在同一个包里发送。
服务然后以键值对的格式回复,带两个新行然后是回复体。HTTP就是一个请求/回复(request/response)协议;不支持“Push”(服务端无缘无故发送数据给使用方),除非你使用一个不同的协议比如Websockets(网页接口)。
当设计你的API时,你应该能够使用允许你查看原始HTTP包的工具工作。比如考虑使用Wireshark。另外,确保你在使用一个允许你读并尽可能多地改变这些域的框架/web服务。

HTTP请求示例

POST /v1/animal HTTP/1.1
Host: api.example.org
Accept: application/json
Content-Type: application/json
Content-Length: 24{"name": "Gir","animal_type": 12
}

HTTP响应示例

HTTP/1.1 200 OK
Date: Wed, 18 Dec 2013 06:08:22 GMT
Content-Type: application/json
Access-Control-Max-Age: 1728000
Cache-Control: no-cache{"id": 12,"created": 1386363036,"modified": 1386363036,"name": "Gir","animal_type": 12
}

【原文地址】https://codeplanet.io/principles-good-restful-api-design/

principles of good restful api design(中文)相关推荐

  1. 如何更好的设计RESTful API(创建公开API)

    https://zhuanlan.zhihu.com/p/24592119?utm_source=tuicool&utm_medium=referral 作者:知秋z 链接:https://z ...

  2. RESTful API的设计原则

    说在前面,这篇文章是无意中发现的,因为感觉写的很好,所以翻译了一下.由于英文水平有限,难免有出错的地方,请看官理解一下.翻译和校正文章花了我大约2周的业余时间,如有人愿意转载请注明出处,谢谢^_^ P ...

  3. [译] RESTful API 设计最佳实践

    https://juejin.im/entry/6844903503953920007 [译] RESTful API 设计最佳实践 阅读 8779 收藏 0 2017-10-16 原文链接: seg ...

  4. 阮一峰RESTful API规范

    一.URL 设计 1.1 动词 + 宾语 RESTful 的核心思想就是,客户端发出的数据操作指令都是"动词 + 宾语"的结构.比如,GET /articles这个命令,GET是动 ...

  5. RESTful API设计技巧经验总结

    原文:RESTful API Design Tips from Experience 作者:Peter Boyer 翻译:雁惊寒 译者注:本文是作者在自己的工作经验中总结出来的RESTful API设 ...

  6. php slim 教程,Slim - 超轻量级PHP Restful API构建框架

    下载源码包: http://www.slimframework.com/ 基于Slim的Restful API Sample: require '/darjuan/Slim/Slim.php'; us ...

  7. api分层内部外部 spring_java - Spring boot restful API分层架构验证 - SO中文参考 - www.soinside.com...

    [今天,我与我们的一位团队成员就Controller和Service层中的RESTful API输入的验证进行了大讨论,我觉得这是提出更大论点的糟糕日子.因此,我们有一个具有分层体系结构的spring ...

  8. 深入探索REST(1):如何设计好的RESTful API?

    REST架构风格最初由Roy T. Fielding(HTTP/1.1协议专家组负责人)在其2000年的博士学位论文中提出.HTTP就是该架构风格的一个典型应用.从其诞生之日开始,它就因其可扩展性和简 ...

  9. 虚拟研讨会:如何设计好的RESTful API?

    http://www.infoq.com/cn/articles/how-to-design-a-good-restful-api/ REST架构风格最初由Roy T. Fielding(HTTP/1 ...

最新文章

  1. Firefox 与 IE 已死?Chrome 一统天下!
  2. mongodb常用管理命令
  3. Nomad技术手册:调度(Scheduling)
  4. 《信息安全系统设计基础》 实验五
  5. mongodb和SQL语句对应查找表
  6. 安卓学习之路之如何显示一个listview列表视图
  7. win7+vs2008+windows mobile6.5.3
  8. Linux下编程获取本地IP地址的常见方法
  9. 递归创建多级文件目录(PHP)
  10. 网站推广效率最高的20种办法
  11. WIFI的下一代:LIFI 可见光无线通信代替微波通信
  12. 计算程序中flag是什么意思,python中flag什么意思
  13. 大数据在金融行业的应用
  14. 数据结构C语言代码实战
  15. R语言之LDA算法应用
  16. TI的CC2530单片机检测不同类型的方波
  17. python numpy 实现与(and),非与(not),或(or),异或(xor)逻辑运算!
  18. X特效 html+css+js
  19. 【评分卡】评分卡入门与创建原则——分箱、WOE、IV、分值分配
  20. 从零开始安装搭建win10与ubuntu20.04双系统开发环境——集安装、配置、软件、美化、常见问题等于一体的——超详细教程

热门文章

  1. 规范的Java注释模板设置
  2. iOS支付宝支付(Alipay)详细接入流程以及项目中遇到的问题分析
  3. Windows开机和关机变慢的原因分析和解决办法
  4. 土壤墒情测量仪有哪些
  5. 密码强度的正则表达式(JavaScript)
  6. 通达信自带公式 阶段强于大盘(C124)
  7. 常用的数据预处理方法
  8. 九龙证券|AIGC彻底火了,概念股全线上涨,走势领先者三连板!
  9. 学习使我快乐学习使我升华只要你爱学习我们就是一辈子的好朋友
  10. 纯HTML+CSS实战之仿微信聊天界面制作