本文主要读者

引言

REST是什么

  集合接口

    因资源

    经特色来操作资源

    起描述的消息

    超媒体即祭状态引擎(HATEOAS)

  无状态

  可缓存

  C-S架构

  分系统

  按需编码(可选)

REST快速提示

  利用HTTP动词表示有意义

  客观之资源名

  XML和JSON

  始建适当粒度的资源

  考虑连通性

定义

  幂等性

  安全

HTTP动词

  GET

  PUT

  POST

  PUT和POST的创建于

  DELETE

资源命名

  资源URI示例

  资源命名的反例

  复数

回到表征

  资源通过链接的而是发现性(HATEOAS续)

    最小化链接推荐

    链接格式

  包装响应

  拍卖跨域问题

    支持CORS

    支持JSONP

询问,过滤与分页

  结果限制

    之所以范围标记进行限

    为此字符串查询参数进行界定

    基于范围的应

  分页

  结果的过滤与排序

    过滤

    排序

服务版本管理

  由此内容商支持版本管理

  当没点名版本时,返回什么版本?

  求不支持的本子

  咦时应该创建一个新本子?

    破坏性的改动

    非破坏性的改

  版本控制应在啊级别出现?

  动Content-Location来加强响应

  带有Content-Type的链接

  招来有支持的本子

    自家应当又支持小只版本?

    弃用

    自我怎么告客户端给弃用的资源?

日期/时间拍卖

  Body内容被之日期/时间序列化

  HTTP
Headers中之日子/时间序列化

保障服务之安康

  身份验证

  传安全

  授权

  应用程序安全

缓存和可伸缩性

  ETag Header

HTTP状态码(前10)

叠加资源

  书籍

  网站

 

本文主要读者

  该最佳实践文档适用于对RESTful
Web服务感兴趣之开发人员,该服务啊跨多只劳务之组件提供了于高之可靠性以及一致性。按照本文的指,可迅速、广泛、公开地为内外部客户以。

  本文中的点规范一致适用于工程师们,他们期望以这些根据最佳实践标准开发的服务。虽然她们更是体贴缓存、代理规则、监听与平安等有关地方,但是该文档能作为同一客包含所有品类服务的总指南。

  另外,通过由这些点规范,管理人员了解及创造公共的、提供高稳定性的服务所待花费的不竭,他们也只是从中受益。

 

引言

  现今曾出雅量关于RESTful
Web服务至上实践的相干资料(详见本文最后之连带文献有)。由于撰文的岁月各异,许多素材中的内容是矛盾的。此外,想使由此翻看文献来了解这种服务之上进是免顶长之。为了了解RESTful这同定义,至少用查阅三及五依照有关文献,而本文将能够助您加快这无异经过——摒弃多余的座谈,最大化地提炼出REST的超级实践与正式。

  与其说REST是一律拟标准,REST更如是一致种规格的汇聚。除了六单第一的格外便没有外的正规化了。实际上,虽然有所谓的“最佳实践”和正规,但这些东西都与宗教斗争一样,在不停地演变。

  本文围绕REST的普遍问题提出了意以及仿食谱式的议论,并通过介绍部分略的背景知识对创建真实处境下的先期生产条件受到同的REST服务提供文化。本文收集了来其它渠道的信,经历了一次次底黄后不断改进。

  但于REST模式是否肯定比SOAP好用本发生比充分争(反之亦然),也许在好几情况下以亟需创造SOAP服务。本文在提及SOAP时并未花较生篇幅来谈谈她的相对优点。相反由于技术与行业在不断进步,我们以持续坚持不懈我们的如果–REST是即时计划web服务之顶尖方式。

  第一有概述REST的意义、设计则与她的非常规之处。第二片段罗列了一些稍微贴士来记忆REST的劳务意见。之后的有些则会重透彻地也web服务创建人员提供部分细节的支撑与讨论,来兑现一个会公开亮在生条件中的大质量REST服务。

 

REST是什么?

  REST架构方式讲述了六种设计则。这些用于架构的计划性则,最早是由Roy
Fielding在外的博士论文中提出并定义了RESTful风格。(详见http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm)

  六独统筹则分别是:

  • 联合接口
  • 无状态
  • 可缓冲
  • C-S架构
  • 分段系统
  • 按需编码

  以下是这些计划则的详实座谈:

统一接口

  统一接口准则定义了客户端与服务端之间的接口,简化和分手了框架结构,这样一来每个片都可独立演化。以下是接口统一之季单标准:

  基于资源

  不同资源需要因此URI来唯一标识。返回给客户端的风味和资源本身在概念上有所不同,例如服务端不会见直接传送一个数据库资源,然而,一些HTML、XML或JSON数据会显示部分数据库记录,如用芬兰语来抒发还是用UTF-8编码则使因请求和服务器实现的底细来支配。

  通过特征来操作资源

  当客户端收到包含元数据的资源的性状时,在闹权力的景象下,客户端都掌握的足够的音信,可以对服务端的资源拓展删改。

  自描述的信

  每条消息都含足够的数目用于确认消息该如何处理。例如要由网络媒体类型(已知晓之只要MIME类型)来确认要调用哪个解析器。响应同样也表明了它的缓存能力。

  超媒体即祭状态引擎(HATEOAS)

  客户端通过body内容、查询串参数、请求头和URI(资源名称)来传送状态。服务端通过body内容,响应码和响应头传送状态为客户端。这项技术为称作超媒体(或超文本链接)。

  除了上述内容外,HATEOS也象征,必要的下链接也可是于含有在回去的body(或头部)中,以供URI来寻觅对象自我还是提到对象。下文将对之进行再详尽的阐发。

  统一接口是每个REST服务计划时的必备准则。

无状态

  正如REST是REpresentational State
Transfer的缩写,无状态不行关键。本质上,这表明了拍卖要所要的状态都包含在求我里,也时有发生或是URI的如出一辙片段、查询串参数、body或头部。URI能够唯一标识每个资源,body中呢蕴含了资源的转态(或转态变更情况)。之后,服务器将进行拍卖,将有关的状态或资源通过头部、状态与应body传递给客户端。

  从事我们顿时同样业的大部分人且习惯使用容器来编程,容器被产生一个“会话”的概念,用于在多个HTTP请求下维持状态。在REST中,如果要于差不多只请求下保持用户状态,客户端必须概括客户端的有所消息来就请求,必要常常重发送请求。自从服务端不待保障、更新或传递会话状态后,无状态性得到了再也老的延展。此外,负载均衡器无需担心和管状态系统里头的对话。

  所以状态及资源间有啊异样?服务器对状态,或者说是应用状态,所关心的触及是以时下对话或请被若就请求所用的多少。而资源,或者说是资源状态,则是概念了资源特点的数,例如存储于数据库被的数目。由此可见,应用状态是是趁客户端与要的改变而改变之多寡。相反,资源状态对于发出请求的客户端的话是不转换的。

  于网络以之某某平等特定岗位及摆放一个回到按钮,是为它仰望而能以一定的顺序来操作也?其实是盖它违反了随便状态的法。有多免遵循无状态原则的案例,例如3-Legged
OAuth,API调用速度限制等。但要么要尽可能保证服务器受到未欲以差不多个请求下保持利用状态。

可缓存

  以万维网上,客户端可缓存页面的响应内容。因此应都许诺隐式或显式的定义为可缓存的,若不足缓存则只要避免客户端在三番五次请后用原来数据还是污染数据来响应。管理得当的复苏存会部分地还是全地除了客户端以及服务端之间的互动,进一步改进性与延展性。

C-S架构

  统一接口使得客户端和服务端相互分开。关注分离意味什么?打个如,客户端不欲仓储数据,数据都留于服务端内部,这样使客户端代码的可移植性得到了升级;而服务端不待考虑用户接口和用户状态,这样一来服务端将越加简明好拓展。只要接口不转,服务端和客户端可单独地进行研发与替换。

分层系统

  客户端通常无法表明自己是直接或者间接与端服务器进行连接。中介服务器可以经启用负载均衡或供共享缓存来提升系统的延展性。分层时一致要考虑安全策略。

按需编码(可选)

  服务端通过传输可实施逻辑给客户端,从而为那个临时拓展和定制功能。相关的例子有编译组件Java
applets和客户端脚本JavaScript。

  遵从上述原则,与REST架构风格保持一致,能让各种分布式超媒体系统具备梦想之自然属性,比如高性能,延展性,简洁,可变性,可视化,可移植性和可靠性。

  提示:REST架构中的规划则遭到,只有按需编码为可选取项。如果某服务违反了另外随意一宗则,严格意思上无克称之为RESTful风格。

 

REST快速提示

  (根据地方提到的六单标准)不管在技术上是免是RESTful的,这里来一对类似REST概念的建议。遵循它,可以兑现还好、更管用的劳务:

行使HTTP动词表示有意义

  任何API的使用者能发送GET、POST、PUT和DELETE请求,它们非常十分程度显而易见了所受告的目的。同时,GET请求不能够改任何秘密的资源数量。测量和跟踪仍可能来,但单单见面更新数据而不会见更新由URI标识的资源数量。

理所当然之资源名

  合理之资源名称或路径(如/posts/23一旦无是/api?type=posts&id=23)可以还显一个告的目的。使用URL查询串来过滤数据是死好之办法,但未应用于固定资源名称。

  适当的资源名称也服务端请求提供上下文,增加服务端API的可理解性。通过URI名称分层地翻看资源,可以给使用者提供一个温馨的、容易理解的资源层次,以在他们之应用程序上用。资源名称应当是名词,避免为动词。使用HTTP方法来指定要的动作有,能叫工作越来越的清。

XML和JSON

  建议默认支持json,并且,除非花费很震惊,否则便以支持json和xml。在好图景下,让使用者仅经过改扩展名.xml和.json来切换类型。此外,对于支持ajax风格的用户界面,一个于包的应是那个有协助的。提供一个受卷入的应,在默认的或者发独立放展名的情下,例如:.wjson和.wxml,表明客户端请求一个为打包的json或xml响应(请参见下的卷入响应)。

  “标准”中对json的渴求特别少。并且这些要求光是语法性质的,无关内容格式和布局。换句话说,REST服务端调用的json响应是商量的相同片段——在正规中没有有关描述。更多关于json数据格式可以于http://www.json.org/上找到。

  关于REST服务受到xml的使,xml的正规化与预约除了利用语法正确的竹签以及文本外没有任何的打算。特别地,命名空间不是吗无应当是于以在REST服务端的光景文中。xml的回重新接近于json——简单、容易看,没有模式及命名空间的底细表现——仅仅是数码与链接。如果她比就再次复杂的话,参看本节的第一段子——使用xml的资本是震惊之。鉴于我们的更,很少有人以xml作为响应。在它吃完全淘汰之前,这是最后一个只是吃得之地方。

创立适当粒度的资源

  同开始,系统中效仿底层应用程序域或数据库架构的API更易受创造。最终,你见面希望用这些服务都做及共同——利用基本上宗底层资源减少通信量。在创造独立的资源后更创更特别粒度的资源,比由更充分的同集中创建于生粒度的资源更爱有。从部分有些之轻定义之资源开始,创建CRUD(增删查改)功能,可以要资源的始建变得更易。随后,你得创建这些根据用例和削减通信量的资源。

设想连通性

  REST的原理之一就是是连通性——通过超媒体链接实现。当在响应中归链接时,api变的更有着从描述性,而以无其常服务端依然可用。至少,接口本身可以吗客户端提供哪些寻找数据的参阅。此外,在经过POST方法创建资源时,还可行使头位置包含一个链接。对于响应中支持分页的汇聚,”first”、
“last”、”next”、和”prev”链接至少是老大实惠之。

 

定义

幂等性

  不要打字面意思来理解什么是幂等性,恰恰相反,这跟某些功能紊乱的世界无关。下面是出自维基百科的解说:

在微机是中,术语幂当用于更宏观地叙述一个操作,一次还是累行该操作发生的结果是同一的。根据使用的上下文,这恐怕发差之意思。例如,在法还是子例程调用有副作用的事态下,意味着当首先调用之后为改的状态为保障无转移。

  从REST服务端的角度来拘禁,由于操作(或服务端调用)是幂等的,客户端好用重新的调用而发生相同的结果——在编程语言中操作像是一个”setter”(设置)方法。换句话说,就是用多独一律之呼吁与用单个请求效果一样。注意,当幂等操作以服务器上发生同样之结果(副作用),响应本身可能是殊之(例如当多独请求中,资源的状态恐怕会见转)。

  PUT和DELETE方法为定义为凡幂等的。查看http请求中delete动词的警戒信息,可以参见下文的DELETE部分。GET、HEAD、OPTIO和TRACE方法从被定义也安全之主意后,也受定义也幂等的。参照下关于安全的段。

安全

  来自维基百科:

片智(例如GET、HEAD、OPTIONS和TRACE)被定义也安的方式,这意味她仅仅让用来信息寻找,而非可知更改服务器的状态。换句话说,它们不会见生出副作用,除了相对来说无害的震慑使日志、缓存、横幅广告还是计数服务等。任意的GET请求,不考虑用状态的上下文,都叫看是安全的。

  总之,安全意味着调用的办法不见面惹副作用。因此,客户端好频繁使用安全之求而不用担心对服务端产生其他副作用。这象征服务端必须遵从GET、HEAD、OPTIONS和TRACE操作的平安概念。否则,除了针对消费端产生模糊外,它还见面招Web缓存,搜索引擎以及另活动代理的问题——这将以服务器上生意想不到的产物。

  根据定义,安全操作是幂等的,因为它们在服务器上发同样之结果。

  安全之法吃实现吗单读操作。然而,安全并无代表服务器必须每次都归相同之应。

 

HTTP动词

  Http动词主要按“统一接口”规则,并提供于咱相应之根据名词的资源的动作。最根本要极端常用之http动词(或者称方法,这样叫可能更恰当些)有POST、GET、PUT和DELETE。这些分别指向应于创建、读取、更新和去(CRUD)操作。也发出广大外的动词,但是以效率比较没有。在这些使比较少之办法中,OPTIONS和HEAD往往利用得又多。

GET

  HTTP的GET方法用于检索(或读取)资源的多寡。在正确的请路径下,GET方法会返回一个xml或者json格式的数,以及一个200之HTTP响应代码(表示是返回结果)。在错误情况下,它通常返回404(不有)或400(错误的请)。

  例如:

*  GET http://www.example.com/customers/12345*
  GET http://www.example.com/customers/12345/orders
  GET http://www.example.com/buckets/sample

  按照HTTP的设计规范,GET(以及附带的HEAD)请求单用于读取数据而不改多少。因此,这种应用方法被认为是平安之。也就是说,它们的调用没有多少修改或者污染之高风险——调用1次等同调用10不成还是没有于调用的效益同样。此外,GET(以及HEAD)是幂等的,这意味使用多只同之恳求和利用单个的呼吁最终都具有相同的结果。

  不要通过GET暴露不安全之操作——它当永远都非可知改改服务器上的其他资源。

PUT

  PUT通常给用来创新资源。通过PUT请求一个都掌握的资源URI时,需要以呼吁的body中蕴含对原来资源的创新数据。

  不过,在资源ID是由客服端而休服务端提供的情下,PUT同样可以让用来创造资源。换句话说,如果PUT请求的URI中富含的资源ID值在服务器上无存在,则用于创造资源。同时呼吁的body中得带有要创建的资源的数目。有人当就会出歧义,所以只有真的用,使用这种办法来创造资源应该给慎用。

  或者我们吧得以于body中提供由客户端定义之资源ID然后使用POST来创造新的资源——假设请求的URI中无含要创造的资源ID(参见下POST的组成部分)。

  例如:

*  PUT http://www.example.com/customers/12345*
  PUT http://www.example.com/customers/12345/orders/98765
  PUT http://www.example.com/buckets/secret\_stuff

  当用PUT操作更新成功时,会回到200(或者返回204,表示回去的body中不含其他内容)。如果采取PUT请求创建资源,成功返回的HTTP状态码是201。响应的body是可选的——如果提供的语句将会晤损耗又多的拉动富。在创立资源时并未必要通过头部的职返回链接,因为客户端就设置了资源ID。请参见下的回到值部分。

  PUT不是一个平安的操作,因为其会窜(或创)服务器上的状态,但她是幂等的。换句话说,如果您以PUT创建或者更新资源,然后再次调用,资源依然在以状态不见面发生变化。

  例如,如果当资源增量计数器中调用PUT,那么是调用方法就是不再是幂等的。这种情形有时候会出,且可能可以证明它是免幂等性的。不过,建议维持PUT请求的幂等性。并强烈建议非幂等性的要使用POST。

POST

  POST请求时给用来创造新的资源,特别是受用来创造于属于资源。从属于资源就属为其他资源(如爸爸资源)的资源。换句话说,当创建一个新资源时,POST请求发送给父资源,服务端负责用新资源以及老子资源开展关联,并分配一个ID(新资源的URI),等等。

  例如:

  POST http://www.example.com/customers
  POST http://www.example.com/customers/12345/orders

  当创建成功时,返回HTTP状态码201,并顺便一个位置头信息,其中带有指向最先创建的资源的链接。

  POST请求既非是安的又无是幂等的,因此它们吃定义为非幂等性资源要。使用简单独一样的POST请求很可能会见招创建两单带有相同信息之资源。

PUT和POST的创立于

  总之,我们建议利用POST来创造资源。当由客户端来控制新资源有什么样URI(通过资源名称或者ID)时,使用PUT:即如客户端知道URI(或资源ID)是啊,则指向该URI使用PUT请求。否则,当由服务器或服务端来控制创造的资源的URI时虽然使POST请求。换句话说,当客户端在创立之前未清楚(或无法掌握)结果的URI时,使用POST请求来创造新的资源。

DELETE

  DELETE很轻了解。它吃用来冲URI标识删除资源。

  例如:

  DELETE http://www.example.com/customers/12345
  DELETE http://www.example.com/customers/12345/orders
  DELETE http://www.example.com/buckets/sample

  当去成功时,返回HTTP状态码200(表示是),同时会顺手一个应体body,body中恐带有了去项之数据(这会占用部分大网带来富),或者封装的应(参见下的返回值)。也可回来HTTP状态码204(表示不管内容)表示没有响应体。总之,可以回状态码204意味着没有响应体,或者返回状态码200还要附带JSON风格的响应体。

  根据HTTP规范,DELETE操作是幂等的。如果您对一个资源拓展DELETE操作,资源就叫移除了。在资源上频繁调用DELETE最终致的结果尚且平等:即资源被移除了。但若是用DELETE的操作用于计数器(资源间),则DETELE将不再是幂等的。如前方所陈述,只要数据没有让更新,统计以及测量的用法依然只是于认为是幂等的。建议非幂等性的资源要使用POST操作。

  然而,这里产生一个有关DELETE幂等性的警告。在一个资源达成第二次等调整用DELETE往往会回来404(未找到),因为该资源就被移除了,所以找不交了。这使DELETE操作不再是幂等的。如果资源是自数据库中删去而休是为简单地记为去,这种情况用适宜让步。

  下表总结发生了首要HTTP的方式以及资源URI,以及推荐的返回值:

HTTP请求

/customers

/customers/{id}

GET

200(正确),用户列表。使用分页、排序和过滤大导航列表。

200(正确),查找单个用户。如果ID没有找到或ID无效则赶回404(未找到)。

PUT

404(未找到),除非您想以整整集合中更新/替换每个资源。

200(正确)或204(无内容)。如果没有找到ID或ID无效则赶回404(未找到)。

POST

201(创建),带有链接到/customers/{id}的位置头信息,包含新的ID。

404(未找到)

DELETE

404(未找到),除非你想去所有集合——通常不受允许。

200(正确)。如果没有找到ID或ID无效则赶回404(未找到)。

 

资源命名

  除了当地采取HTTP动词,在创建一个得以理解的、易于使的Web服务API时,资源命名可以说凡是无与伦比具有争议与无限要害的定义。一个好之资源命名,它所对应的API看起又直观并且爱使。相反,如果命名不好,同样的API会吃人口深感非常笨而难以明白以及使用。当您要也汝的新API创建资源URL时,这里出一些有些技巧值得借鉴。

  从实质上讲话,一个RESTFul
API最终都得以给略去地当是相同积聚URI的集合,HTTP调用这些URI以及有之所以JSON和(或)XML表示的资源,它们被生众多富含了互动关联的链接。RESTful的只是寻址能力要依赖URI。每个资源都发和好之地方或URI——服务器能够提供的各一个立竿见影的消息还足以看成资源来明。统一接口的极有地经过URI和HTTP动词的结来解决,并可利用专业以及约定。

  以支配你系统受到若利用的资源时,使用名词来命名这些资源,而无是因此动词或动作来定名。换句话说,一个RESTful
URI应该干到一个现实的资源,而无是涉及到一个动作。另外,名词还富有部分动词没有底性,这也是另外一个肯定的要素。

  一些资源的例证:

  • 系统的用户
  • 生登记的课
  • 一个用户帖子的时轴
  • 体贴入微其他用户的用户
  • 同样首关于骑马的章

  服务套件中的每个资源最少有一个URI来标识。如果这个URI能表示一定之义并且能够尽量描述她所代表的资源,那么它便是一个最好好之命名。URI应该具有可预测性和分层结构,这将推增强其的可理解性和可用性的:可预测指的凡资源应该跟名保持一致;而分指的是数据具有涉上的组织。这并非REST规则或规范,但是它加重了针对性API的概念。

  RESTful
API是供于消费端的。URI的号及结构应当将它们所表达的意义传达给消费者。通常我们大不便掌握数码的鄂是啊,但是由君的数达你应当好有或去尝尝找到要回到给客户端的数额是啊。API是吗客户端而设计的,而无是啊汝的数量。

  假设我们今天而描述一个席卷客户、订单,列表项,产品等功用的订单系统。考虑一下我们欠如何来描述在这服务遭遇所干到之资源的URIs:

资源URI示例

  为了在系面临插(创建)一个初的用户,我们得动用:

  POST http://www.example.com/customers

 

  读取编号为33245之用户信息:

  GET http://www.example.com/customers/33245

  使用PUT和DELETE来请求相同的URI,可以创新与去数据。

 

  下面是对准成品有关的URI的部分建议:

  POST http://www.example.com/products

  用于创造新的成品。

 

  GET|PUT|DELETE http://www.example.com/products/66432

  分别用于读取、更新、删除编号为66432之产品。

 

  那么,如何呢用户创建一个新的订单也?

  一栽方案是:

  POST http://www.example.com/orders

  这种办法得以据此来创造订单,但欠相应的用户数量。

  

  因我们怀念啊用户创建一个订单(注意之间的关系),这个URI可能不足够直观,下面这URI则还鲜明一些:

  POST http://www.example.com/customers/33245/orders

  现在咱们知道她是也编号33245底用户创建一个订单。

 

  那下面这请返回的是什么吗?

  GET http://www.example.com/customers/33245/orders

  可能是一个号吧33245的用户所创办或者有的订单列表。注意:我们可遮挡对拖欠URI进行DELETE或PUT请求,因为其的操作对象是一个汇聚。

 

  继续深入,那下面这个URI的要而意味着什么呢?

  POST http://www.example.com/customers/33245/orders/8769/lineitems

  可能是(为编号33245底用户)增加一个号吧8769的订单条目。没错!如果利用GET方式呼吁是URI,则会回到这个订单的具有条条框框。但是,如果这些条款与用户信息无关,我们将会晤供POST
www.example.com/orders/8769/lineitems
这个URI。

  从返回的这些条款来拘禁,指定的资源或会见生出多单URIs,所以我们或许也得而提供这样一个URI
GET
http://www.example.com/orders/8769
,用来当无晓用户ID的状态下基于订单ID来询问订单。

 

  更进一步:

  GET http://www.example.com/customers/33245/orders/8769/lineitems/1

  可能单回跟个订单被的第一个条文。

  现在而当了解啊是分开层构造了。它们并无是严格的规则,只是为保于您的劳务中这些强制的构造会再次易给用户所掌握。与有着软件开发中之技巧一样,命名是马到成功之基本点。

  

  多看有些API的示范并学会控制这些技巧,和而的队友一起来宏观而API资源的URIs。这里发出局部APIs的事例:

  • Twitter: https://dev.twitter.com/docs/api
  • Facebook: http://developers.facebook.com/docs/reference/api/
  • LinkedIn: https://developer.linkedin.com/apis

资源命名的反例

  前面我们已讨论了局部适中的资源命名的事例,然而有时有反面的例子也颇有教育意义。下面是有未极端具有RESTful风格的资源URIs,看起比混乱。这些都是左的例子! 

  首先,一些serivices往往采取单一的URI来指定服务接口,然后经过查询参数来指定HTTP请求的动作。例如,要更新编号12345之用户信息,带有JSON
body的请求或是这么:

  GET
http://api.example.com/services?op=update\_customer&id=12345&format=json

  尽管地方URL中之”services”的此节点是一个名词,但以此URL不是自从说的,因为对有的乞求而言,该URI的层级结构都是平的。此外,它用GET作为HTTP动词来施行一个翻新操作,这简直就是倒人类(甚至是险象环生的)。

  下面是另外一个更新用户之操作的例子:

  GET http://api.example.com/update\_customer/12345

  以及它们的一个变种:

  GET http://api.example.com/customers/12345/update

  你晤面常常见到于另外开发者的劳动套件中有成百上千这么的用法。可以见到,这些开发者试图去创造RESTful的资源名称,而且早已有了有前进。但是若还能够分辨出URL中之动词短语。注意,在这个URL中我们无需”update”这个词,因为咱们好因HTTP动词来形成操作。下面这URL正好说明了当下一点:

  PUT http://api.example.com/customers/12345/update

  这个要又设有PUT和”update”,这会针对客有迷惑!这里的”update”指的是一个资源也?因此,这里我们费些口舌也是愿意而能够领略……

复数

  让咱们来讨论一下复数和“单数”的争执…还没听说过?但这种争议确实在,事实上它们可概括为这个题材……

  以你的层级结构中URI节点是否要让取名也单数或复数形式吗?举个例子,你用来寻觅用户资源的URI的命名是否需要像下这样:

  GET http://www.example.com/customer/33245

  或者:

  GET http://www.example.com/customers/33245

  两栽方法还没问题,但通常咱们都见面选下复数命名,以让你的API
URI在具有的HTTP方法被保持一致。原因是基于这样同样种植考虑:customers是劳动套件中的一个聚,而ID33245底之用户则是是集中之里一个。

  按照这个规则,一个使复数形式之多节点的URI会是这样(注意粗体部分):

  GET
http://www.example.com/**customers**/33245/**orders**/8769/**lineitems**/1

  “customers”、“orders”以及“lineitems”这些URI节点都采用的凡复数形式。

  这意味你的每个根资源就待简单独着力的URL就好了,一个用以创造集合内的资源,另一个之所以来因标识符获取、更新与去资源。例如,以customers为条例,创建资源得以行使下的URL进行操作:

  POST http://www.example.com/customers

  而读取、更新和去资源,使用下的URL操作:

  GET|PUT|DELETE http://www.example.com/customers/{id}

  正而前提到的,给得的资源或有多个URI,但当一个无比小的整的增删改查功能,利用有限独简单的URI来处理便足足了。

  或许你见面咨询:是否当微情况下复数没有意义?嗯,事实上是这般的。当没凑概念的早晚(此时复数没有意义)。换句话说,当资源就发生一个底情下,使用单数资源名称也是好的——即一个纯的资源。例如,如果发生一个单一的共同体布局资源,你可利用一个单数名称来代表:

  GET|PUT|DELETE http://www.example.com/configuration

  注意这里少configuration的ID以及HTTP动词POST的用法。假设每个用户发生一个布置来说,那么这个URL会是这么:

  GET|PUT|DELETE
http://www.example.com/customers/12345/configuration

  同令人瞩目这里没点名configuration的ID,以及无于定POST动词的用法。在马上半个例中,可能吧会见有人以为用POST是可行之。好吧…

 

回表征

  正使前方提到的,RESTful接口支持多资源特点,包括JSON和XML,以及被装进的JSON和XML。建议JSON作为默认表征,不过服务端应该允许客户端指定其他表征。

  对于客户端请求的表征格式,我们好于Accept头通过文件扩展名来进行点名,也可以经过query-string等其他措施来指定。理想状态下,服务端可以支撑具备这些艺术。但是,现在正规更赞成于经类似于文件扩展名的艺术来展开点名。因此,建议服务端至少得支持采取文件扩展名的方法,例如“.json”,“.xml”以及她的卷入版本“.wjon”,“.wxml”。

  通过这种方法,在URI中指定返回表征的格式,可以提高URL的可见性。例如,GET
http://www.example.com/customers.xml
用赶回customer列表的XML格式的性状。同样,GET
http://www.example.com/customers.json
用回一个JSON格式的特性。这样,即使是以尽基础之客户端(例如“curl”),服务应用起来吧会越加便捷。推荐用这种艺术。

  此外,当url中从未含格式说明时,服务端应该归默认格式的表征(假设为JSON)。例如:

  GET http://www.example.com/customers/12345

  GET http://www.example.com/customers/12345.json

  以上两者返回的ID为12345底customer数据都为JSON格式,这是服务端的默认格式。

  GET http://www.example.com/customers/12345.xml

  如果服务端支持的话,以上要返回的ID为12345的customer数据为XML格式。如果该服务器不支持XML格式的资源,将回来一个HTTP
404之左。

  使用HTTP
Accept头被大认为是如出一辙种更优雅的方式,并且符合HTTP的正规以及含义,客户端好经这种措施来报告HTTP服务端它们可支持的数据类型有安。但是,为了利用Accept头,服务端要同时支持封装和非封装的响应,你不能不实现从定义之类——因为这些格式不是业内的路。这大大增加了客户端与服务端的纷繁。请参见RFC
2616的14.1节有关Accept头的详细信息(http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1)。使用文件扩展名来指定数量格式是极其简便直接的法门,用极少之字符就得成功,并且支持脚本操作——无需利用HTTP头。

  通常当我们提到REST服务,跟XML是毫不相关的。即使服务端支持XML,也几乎无人提议在REST中采用XML。XML的正规和公约在REST中无太适用。特别是它们并命名空间都无,就再也无欠在RESTful服务体系中采取了。这只会使业务变得再复杂。所以回来的XML看起再次如JSON,它概括容易读,没有模式以及命名空间的限,换句话来说是凭标准的,易于解析。

资源通过链接的可发现性(HATEOAS续)

  REST指导原则之一(根据统一接口规范)是application的状态通过hypertext(超文本)来传。这就是我们便所说的Hypertext
As The Engine of Application State
(即HATEOAS,用超文本来当应用程序状态机),我们以“REST是什么”同省中吗关乎过。

  根据Roy
Fielding在他的博客中之描述(http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertextdriven),REST接口中最为着重之有是超文本的应用。此外,他尚指出,在让有其他有关的音讯前,一个API应该是可用和而了解的。也就是说,一个API应当可以经该链接导航及数量的依次部分。不建议才回纯数据。

  不过当下之业界先驱们连没有经常用这种做法,这体现了HATEOAS仅仅在成熟度模型中之使用率还胜似。纵观众多之服务体系,它们多返回重新多的多寡,而回到的链接却分外少(或者没)。这是负Fielding的REST约定的。Fielding说:“信息之各一个不过寻址单元都携一个地点……查询结果应该呈现吗一个包含摘要信息之链接清单,而无是目标往往组。”

  另一方面,简单粗暴地用一切链接集合返回会大大影响网络带来富。在实际状况被,根据所欲的规则还是使用状况,API接口的通信量要因服务器响应中超文本链接所蕴涵的“摘要”数量来抵消。

  同时,充分利用HATEOAS可能会见大增实现的复杂,并针对性服务客户端有强烈的承受,这一定给降低了客户端与服务器端开发人员的生产力。因此,当务之急是使平衡超链接服务实施与现有可用资源之间的问题。

  超链接太小化的做法是以最特别限度地压缩客户端和服务器之间的耦合的同时,提高服务端的可用性、可操纵性和可理解性。这些最小化建议是:通过POST创建资源并由GET请求返回集合,对于发出分页的情状后我们见面干。

太小化链接推荐

  以create的用例中,新建资源的URI(链接)应该当Location响应头中回到,且应中心是空的——或者仅含新建资源的ID。

  对于自服务端返回的特性集合,每个表征应该在它的链接集合中携带一个不过小的“自身”链接属性。为了方便分页操作,其它的链接可以在一个单独的链接集合中归,必要时好蕴涵“第一页”、“上同页”、“下一致页”、“最后一页”等信息。

  参照下文链接格式一部分的事例获取更多信息。

链接格式

  参照整个链接格式的正经,建议遵守一些看似Atom、AtomPub或Xlink的作风。JSON-LD也对,但并不曾叫广大运用(如果都为用了)。目前正规最广大的章程是动带有”rel”元素以及带有资源整体URI的”href”元素的Atom链接格式,不包含其他身份验证或询问字符串参数。”rel”元素得以蕴涵标准值”alternate”、”related”、”self”、”enclosure”和”via”,还有分页链接的“第一页”、“上平等页”、“下一致页”,“最后一页”。在急需时得以由定义并累加应用它们。

  一些XML
Atom格式的概念对用JSON格式表示的链接来说是没用的。例如,METHOD属性对于一个RESTful资源来说是免待之,因为对于一个加以的资源,在拥有支持的HTTP方法(CRUD行为)中,资源的URI都是一样的——所以单独列有这些是从未有过必要的。

  让咱们选一些具体的事例来更说明及时或多或少。下面是调用创建新资源的请求后底应:

  POST http://api.example.com/users

  下面是作应头集合中蕴藏创建新资源的URI的Location部分:

HTTP/1.1 201 CREATED 
Status: 201 
Connection: close 
Content-Type: application/json; charset=utf-8 
Location: http://api.example.com/users/12346

  返回的body可以为空,或者隐含一个让包的应(见下文封装响应)。

  下面的例证通过GET请求获取一个勿包含分页的风味集合的JSON响应:

{
  "data": [
    {
      "user_id": "42",
      "name": "Bob",
      "links": [
        {
          "rel": "self",
          "href": "http://api.example.com/users/42"
        }
      ]
    },
    {
      "user_id": "22",
      "name": "Frank",
      "links": [
        {
          "rel": "self",
          "href": "http://api.example.com/users/22"
        }
      ]
    },
    {
      "user_id": "125",
      "name": "Sally",
      "links": [
        {
          "rel": "self",
          "href": "http://api.example.com/users/125"
        }
      ]
    }
  ]
}

  注意,links数组中之各级一样项都带有一个对“自身(self)”的链接。该数组还可能还蕴含其他关系,如children、parent等。

  最后一个事例是经GET请求获取一个暗含分页的特色集合的JSON响应(每页显示3桩),我们吃起第三页的多少:

{
  "data": [
    {
      "user_id": "42",
      "name": "Bob",
      "links": [
        {
          "rel": "self",
          "href": "http://api.example.com/users/42"
        }
      ]
    },
    {
      "user_id": "22",
      "name": "Frank",
      "links": [
        {
          "rel": "self",
          "href": "http://api.example.com/users/22"
        }
      ]
    },
    {
      "user_id": "125",
      "name": "Sally",
      "links": [
        {
          "rel": "self",
          "href": "http://api.example.com/users/125"
        }
      ]
    }
  ],
  "links": [
    {
      "rel": "first",
      "href": "http://api.example.com/users?offset=0&limit=3"
    },
    {
      "rel": "last",
      "href": "http://api.example.com/users?offset=55&limit=3"
    },
    {
      "rel": "previous",
      "href": "http://api.example.com/users?offset=3&limit=3"
    },
    {
      "rel": "next",
      "href": "http://api.example.com/users?offset=9&limit=3"
    }
  ]
}

  在斯事例中,响应中用来分页的links集合中的诸一样宗都富含一个针对“自身(self)”的链接。这里恐怕还见面时有发生部分涉及到集结的另外链接,但犹和分页本身无关。简而言之,这里有零星独地方含有links。一个虽是data对象吃所蕴含的会师(这个吧是接口要回去给客户端的数码表征集合),其中的各国一样桩至少要连一个对准“自身(self)”的links集合;另一个虽是一个独立的靶子links,其中包括同分页相关的链接,该片段的始末适用于所有集合。

  对于经POST请求创建资源的景况,需要以响应头被蕴含一个干新建对象链接的Location

包响应

   服务器可以在响应中并且返回HTTP状态码和body。有好多JavaScript框架没有管HTTP状态响应码返回给最终的开发者,这频繁会造成客户端无法根据状态码来确定具体的行为。此外,虽然HTTP规范着发出格外多种响应码,但是频繁只有发少数客户端会关注这些——通常大家只在乎”success”、”error”或”failture”。因此,将响应内容以及应状态码封装在蕴藏响应信息之特色着,是生必要的。

  OmniTI
实验室有这般一个建议,它于号称JSEND响应。更多信息要参考http://labs.omniti.com/labs/jsend。另外一个提案是由Douglas
Crockford提出的,可以查此http://www.json.org/JSONRequest.html。

  这些提案在实践中并无了含所有的景象。基本上,现在不过好之做法是遵照以下属性封装常规(非JSONP)响应:

  • code——包含一个整数型的HTTP响应状态码。
  • status——包含文本:”success”,”fail”或”error”。HTTP状态响应码在500-599间也”fail”,在400-499内吧”error”,其它都为”success”(例如:响应状态码为1XX、2XX与3XX)。
  • message——当状态值为”fail”和”error”时有效,用于展示错误信息。参照国际化(il8n)标准,它可涵盖信息号或者编码,可以但含其中一个,或者又寓并据此分隔符隔开。
  • data——包含响应的body。当状态值为”fail”或”error”时,data就包含错误原因要深名称。

  下面是一个回success的包裹响应:

{
  "code": 200,
  "status": "success",
  "data": {
    "lacksTOS": false,
    "invalidCredentials": false,
    "authToken": "4ee683baa2a3332c3c86026d"
  }
}

  返回error的卷入响应:

{
  "code": 401,
  "status": "error",
  "message": "token is invalid",
  "data": "UnauthorizedException"
}

  这简单单包裹响应对应之XML如下:

<response>
    <code>200</code>
    <status>success</status>
    <data class="AuthenticationResult">
        <lacksTOS>false</lacksTOS>
        <invalidCredentials>false</invalidCredentials>
        <authToken>1.0|idm|idm|4ee683baa2a3332c3c86026d</authToken>
    </data>
</response>

  和:

<response>
    <code>401</code>
    <status>error</status>
    <message>token is invalid</message>
    <data class="string">UnauthorizedException</data>
</response>

拍卖跨域问题

   我们且闻讯了有关浏览器的同源策略要同源性需求。它凭借的是浏览器只能请时正显示的站点的资源。例如,如果手上方显示的站点是www.Example1.com,则该站点不可知针对www.Example.com倡导呼吁。显然这会影响站点访问服务器的方法。

  时发出三三两两单给大规模接受之支持跨域请求的道:JSONP和跨域资源共享(CORS)。JSONP或“填充的JSON”是平种植下模式,它提供了一个方式要来自不同域中的服务器的数。其行事章程是从服务器返回任意的JavaScript代码,而未是JSON。客户端的响应由JavaScript解析器进行辨析,而非是直接解析JSON数据。另外,CORS是一律种web浏览器的技艺标准,它也web服务器定义了千篇一律种艺术,从而允许服务器的资源可以为不同域的网页访问。CORS被看作是JSONP的摩登替代品,并且可给有现代浏览器支持。因此,不建议利用JSONP。任何情况下,推荐选择CORS。

支持CORS

  以服务端实现CORS很简单,只需要在殡葬响应时顺手HTTP头,例如: 

Access-Control-Allow-Origin: *

  只有在数是官使用的景下才会以访问来源设置也”*”。大多数状态下,Access-Control-Allow-Origin头应该指定哪些域好倡导一个CORS请求。只有用跨域访问的URL才装CORS头。

Access-Control-Allow-Origin: http://example.com:8080
http://foo.example.com

  以上Access-Control-Allow-Origin头中,被安装为单同意让信赖的处可以拜。

Access-Control-Allow-Credentials: true

  只在需要时才祭方面是header,因为如果用户就报到的话,它会以发送cookies/sessions。

  这些headers可以透过web服务器、代理来拓展布局,或者由服务器本身发送。不引进以服务端实现,因为好不利索。或者,可以行使方面的亚种植艺术,在web服务器上部署一个所以空格分隔的地方的列表。更多关于CORS的情好参考这里:http://enable-cors.org/。

支持JSONP

  JSONP通过采取GET请求避开浏览器的克,从而实现对有服务的调用。其行事原理是伸手求方在呼吁的URL上加加一个字符串查询参数(例如:jsonp=”jsonp_callback”),其中“jsonp”参数的价值是JavaScript函数名为,该函数在生响应返回时拿会晤为调用。

  由于GET请求被从来不含呼吁求体,JSONP在运时有着严重的局限性,因此数据要经过字符串查询参数来传递。同样的,为了支持PUT,POST和DELETE方法,HTTP方法要也经过字符串查询参数来传递,类似_method=POST这种样式。像这么的HTTP方法传送方式是勿引进应用的,这会吃服务处于安全风险之中。

  JSONP通常在有些非支持CORS的老旧浏览器被使,如果要转化支持CORS的,会影响所有服务器的架构。或者我们啊可以经代理来落实JSONP。总之,JSONP正在给CORS所代表,我们应当尽可能地运CORS。

  为了在服务端支持JSONP,在JSONP字符串查询参数传递时,响应必须要履以下这些操作:

  1. 响应体必须封装成一个参数传递给jsonp中指定的JavaScript函数(例如:jsonp_callback(“<JSON
    response body>”))。
  2. 老返回HTTP状态码200(OK),并且将真的状态作为JSON响应中的一样组成部分归。

  另外,响应体中经常要含有响应头。这让JSONP回调方法要依据响应体来规定响应处理方式,因为她本身无法获悉真实的响应头和状态值。

  下面的事例是比照上述措施封装的一个返error状态的jsonp(注意:HTTP的响应状态是200):

jsonp_callback("{'code':'404', 'status':'error','headers':[],'message':'resource XYZ not
found','data':'NotFoundException'}")

  成功创造后底应类似于如此(HTTP的应状态仍是200):

jsonp_callback("{'code':'201', 'status':'error','headers':
[{'Location':'http://www.example.com/customers/12345'}],'data':'12345'}")

 

查询,过滤与分页

  对于大数据集,从带宽的角度来拘禁,限制返回的数据量是可怜关键的。而自从UI处理的角度来拘禁,限制数据量也一如既往举足轻重,因为UI通常只能展现大数量集中的一模一样有点片段数据。在数据集的增长速度不确定的景象下,限制默认返回的数据量是大有必不可少之。以Twitter为条例,要取有用户之推文(通过个人主页的岁月轴),如果无专门指定,请求默认只见面回20漫长记下,尽管系统最多得回到200修记下。

  除了限制返回的数据量,我们尚得考虑怎样对天意据集进行“分页”或下拉滚动操作。创建数量的“页码”,返回大数量列表的已经了解片段,然后标出数据的“前无异页”和“后同页”——这同样作为让誉为分页。此外,我们或啊要指定响应中将包含如何字段或性质,从而限制返回值的数,并且我们期望最后能够透过一定值来开展查询操作,并针对回到值进行排序。

  有少数种植要的法子来而限定查询结果跟实行分页操作。首先,我们可成立一个目方案,它可因页码为导向(请求中一旦让有各一样页的记录数及页码),或者坐记录也导向(请求中直接让闹第一长记下以及最终一漫长记下)来规定返回值的开局位置。举个例子,这半种植艺术分别代表:“给闹第五页(假设每页有20长达记下)的笔录”,或“给出第100暨第120长条之笔录”。

  服务端将因运作机制来进行切分。有些UI工具,比如Dojo
JSON会选择模仿HTTP规范使用字节范围。如果服务端支持out of
box(即开箱即用效应),则前端UI工具和后端服务中无需任何移,这样使起来会格外有利。

  下文将介绍一种植艺术,既能支持Dojo这样的分页模式(在呼吁求头中于有记录的克),也会支撑采取字符串查询参数。这样一来服务端将更换得更其灵敏,既好利用类似Dojo一样先进的UI工具集,也堪运用简易直接的链接和标签,而无论是需再为这多复杂的支付工作。但如服务不直支持UI功能,可以设想不要以呼吁求头中为有记录范围。

  要专门指出的凡,我们并无引进以装有服务中使查询、过滤与分页操作。并无是具资源还默认支持这些操作,只有少数特定的资源才支撑。服务以及资源的文档应当说明如何接口支持这些扑朔迷离的功力。

结果限制

  “给出第3及第55长的笔录”,这种求数据的办法与HTTP的字节范围规范更平等,因此我们可为此其来标识Range
header。而“从第2长条记下开始,给出最为多20长记下”这种方法又易于阅读与掌握,因此我们平常会为此字符串查询参数的不二法门来表示。

  综上所述,推荐既支持用HTTP Range
header,也支撑以字符串查询参数——offset(偏移量)和limit(限制),然后以服务端对响应结果开展限制。注意,如果又支持就简单栽艺术,那么字符串查询参数的优先级要超过Range
header。

  这里您或许会见出只问题:“这有限种植方式效果相似,但是返的数量未完全一致。这会无见面让人口歪曲呢?”恩…这是鲜单问题。首先使回的是,这诚然会给丁歪曲。关键是,字符串查询参数看起更清晰易懂,在构建与分析时进一步方便。而Range
header则还多是由机器来以(偏向于底层),它更是切合HTTP使用专业。

  总之,解析Range
header的劳作会大增复杂度,相应的客户端在构建请求时也需开展有处理。而采用单独的limit和offset参数会越来越爱了解与构建,并且不需针对开发人员有重新多之渴求。

为此范围标记进行限制

  当用HTTP header而休是字符串查询参数来博取记录的克时,Ranger
header应该经过以下内容来指定范围: 

  Range: items=0-24

  注意记录是从0开始的连日字段,HTTP规范着证实了何等使用Range
header来请求字节。也就是说,如果一旦告数据集中的第一久记下,范围应该从0开始算从。上述的求将会返回前25个记录,假而数据集中至少发生25漫漫记下。

  而当服务端,通过检查请求的Range
header来确定该归哪些记录。只要Range
header存在,就会见发生一个简单易行的正则表达式(如”items=(\d+)-(\d+)”)对那开展剖析,来获得要找的范围值。

从而字符串查询参数进行限定

  字符串查询参数为视作Range
header的替代选择,它利用offset和limit作为参数名为,其中offset代表要查询的首先条记下编号(与上述的用于范围标记的items第一个数字相同),limit代表记录之顶老条数。下面的事例返回的结果以及上述用范围标记的例证一样:

  GET http://api.example.com/resources?offset=0&limit=25

  Offset参数的价和Range
header中之近乎,也是从0开始盘算。Limit参数的价值是回来记录的无限要命数目。当字符串查询参数中未指定limit时,服务端应当让闹一个欠省的最酷limit值,不过这些参数的使用还急需以文档中展开认证。

根据范围之应

  对一个因范围之请来说,无论是通过HTTP的Range
header还是经过字符串查询参数,服务端都应该产生一个Content-Range
header来响应,以标明返回记录之条数和总记录数:

  Content-Range: items 0-24/66

  注意这里的到底记录数(如本例中之66)不是从0开始计算的。如果要要数据汇总之最后几乎长长的记下,Content-Range
header的情节应该是如此:

  Content-Range: items 40-65/66

  根据HTTP的正经,如果响应时究竟记录数未知或难以计算,也可就此星号(”*”)来代表(如本例中的66)。本例中响应头也可这样形容:

  *Content-Range: items 40-65/**

  不过如果顾,Dojo或有别的UI工具或无支持该符号。

分页

  上述措施通过请求方指定数据集的限定来限制返回结果,从而实现分页功能。上面的例证中总共发66修记下,如果各页25条记下,要出示第二页数据,Range
header的始末如下:

  Range: items=25-49

  同样,用字符串查询参数表示如下:

  GET …?offset=25&limit=25

  服务端会相应地回到一组数据,附带的Content-Range header内容如下:

  Content-Range: 25-49/66

  以大部分状况下,这种分页方式还没问题。但有时候会起这种情景,就是要回的笔录数据无法直接表示成数据汇总之行号。还有即使是来若干数据集的变很快,不断会发生新的数码插入到多少汇总,这样自然会招致分页出现问题,一些还的多寡或者会见油然而生于不同之页中。

  按日期排列的数据集(例如Twitter
feed)就是一样种植普遍的景。虽然您还是得本着数码进行分页,但有时候用”after”或”before”这样的重中之重字并同Range
header(或者与字符串查询参数offset和limit)配合来贯彻分页,看起会更为简明易掌握。

  例如,要赢得给定时间穿的前方20久评论:

  GET
http://www.example.com/remarks/home\_timeline?after=&lt;timestamp&gt; 

  Range: items=0-19

  GET
http://www.example.com/remarks/home\_timeline?before=&lt;timestamp&gt; 

*  Range: items=0-19*

  用字符串查询参数表示为:

  GET
http://www.example.com/remarks/home\_timeline?after=&lt;timestamp&gt;&offset=0&limit=20 

*  GET
http://www.example.com/remarks/home\_timeline?before=&lt;timestamp&gt;&offset=0&limit=20*

  有关以不同状况对日穿的格式化处理,请参见下文的“日期/时间处理”。

  如果要时尚未点名要回来的多少范围,服务端返回了同样组默认数据或者限制的极度老数据集,那么服务端同时也应当当返回结果吃涵盖Content-Range
header来和客户端进行确认。以地方个人主页的年华轴为例,无论客户端是不是指定了Range
header,服务端每次都只是回20漫漫记下。此时,服务端响应的Content-Range
header应该包含如下内容:

  Content-Range: 0-19/4125

  或 *Content-Range: 0-19/**

结果的过滤和排序

  针对返回结果,还欲考虑什么当服务端对数据开展过滤和排,以及怎样仍指定的一一对子数据进行查找。这些操作可以跟分页、结果限制,以及字符串查询参数filter和sort等相互结合,可以实现强大的数据检索功能。

  再强调平等糟糕,过滤与排序都是扑朔迷离的操作,不需要默认提供于拥有的资源。下文将介绍如何资源需要提供过滤与排序。

过滤

  以本文中,过滤被定义也“通过特定的规范来确定要使返回的数额,从而减少返回的多少”。如果服务端支持一模拟完整的于运算符和错综复杂的规格配合,过滤操作将更换得相当复杂。不过我们通常会下一些简练的表达式,如starts-with(以…开始)或contains(包含)来进展匹配,以确保返回数据的完整性。

  以咱们开始谈论过滤的字符串查询参数之前,必须先行清楚怎么要用单个参数而休是差不多个字符串查询参数。从根本上来说是以减少参数名称的撞。我们曾生offsetlimitsort(见下文)参数了。如果可能的语句还会发jsonpformat标识符,或许还会见发生afterbefore参数,这些都是于本文屡遭干过的字符串查询参数。字符串查询中运用的参数越多,就愈可能导致参数名称的撞,而利用单个过滤参数则会拿闯之可能降低到低于。

  此外,从服务端也生爱就通过单个的filter参数来判定请求方是否要数过滤效果。如果查询需要的复杂度增加,单个参数将再度富有灵活性——可以协调建平等模仿功能一体化的询问语法(详见下文OData注释或看http://www.odata.org)。

  通过引入一组大的、公认的分隔符,用于过滤的表达式可以因稀直观的样式让运。用这些分隔符来设置过滤查询参数的价,这些分隔符所创建的参数名/值对能够更爱地让服务端解析并提高数据查询的性质。目前早已部分分隔符包括用来分隔每个过滤短语的竖线(”|”)和用来分隔参数名叫与价值的对仗冒号(”::”)。这套分隔符足够唯一,并符合大多数场面,同时用她来构建的字符串查询参数为愈来愈便于掌握。下面用因此一个简单的例子来介绍她的用法。假设我们想只要为名吧“Todd”的用户等发送请求,他们停下在丹佛,有着“Grand
Poobah”之如。用字符串查询参数实现的请求URI如下:

  GET
http://www.example.com/users?filter="name::todd|city::denver|title::grand
poobah”

  双冒号(”::”)分隔符将属于性名和价值分开,这样属性值就会包含空格——服务端能再便于地起属于性值中剖析出分隔符。

  注意查询参数名/值对面临之习性名要和服务端返回的特性名相匹配。

  简单而中。有关大小写敏感的问题,要基于具体情况来拘禁,但总的来说,在毫不关心大小写的情事下,过滤效果可以老好地运行。若查询参数名/值对面临的属于性值未知,你啊得为此星号(”*”)来代替。

  除了简单的表达式和通配符之外,若使开展重复复杂的询问,你必须要引入运算符。在这种状态下,运算符本身也是属于性值的平等片,能够给服务端解析,而不是成属性名的一模一样部分。当需要复杂的query-language-style(查询语言风格)功能时,可参看Open
Data Protocol (OData) Filter System Query
Option说明中的询问概念(详见http://www.odata.org/documentation/uriconventions#FilterSystemQueryOption)。

排序

  排序决定了自服务端返回的记录的次第。也就算是本着响应中的多长条记下进行排序。

  同样,我们这里仅仅考虑部分比较简单的情。推荐下排序字符串查询参数,它含了平等组用分隔符分隔的属性名。具体做法是,默认对每个属性名以升序排列,如果属性名有前缀”-“,则按照降序排列。用竖线(”|”)分隔每个属性名,这跟眼前过滤效果受到的参数名/值对之做法无异于。

  举个例证,如果我们怀念以用户之姓氏和名进行升序排序,而针对性雇佣时间进行降序排序,请求将是这么的:

  GET
http://www.example.com/users?sort=last\_name|first\_name|-hire\_date

  再次强调一下,查询参数名/值对受之性质名要和服务端返回的习性名相匹配。此外,由于排序操作比较复杂,我们只是对需之资源提供排序功能。如果需要的话也堪以客户端对有些的资源集合进行排。

 

劳版本管理

   坦率地说,一说到本就会见受人以为甚不方便,很辛苦,不绝容易,甚至会见让人当难受——因为及时会增多API的复杂度,并而可能会见对客户端起局部影响。因此,在API的宏图受到如尽量避免多单例外之本子。

  不支持版本,不以版本控制作为糟糕之API设计之依赖性。如果你在APIs的筹划被引入版本,这迟早且见面为您捉狂。由于返回的数额经过JSON来见,客户端会由于不同的本要接受至不同的特性。这样即使见面是有的题材,如由内容我及认证规则者改变了一个曾经存在的习性之义。

  当然,我们鞭长莫及避免API可能当好几时候需要改返回数据的格式和内容,而当时吗用导致消费端的部分变,我们相应避免进行一些要的调动。将API进行版本化管理是避这种根本变动的如出一辙栽中方式。

经内容商支持版本管理

  以往,版本管理通过URI本身的本号来好,客户端在请的URI中标明要获取之资源的版本号。事实上,许多良商家要Twitter、Yammer、Facebook、Google等常常于他们的URI里使用版本号。甚至像WSO2这样的API管理工具也会见以它们的URLs中要求版本号。

  面向REST原则,版本管理技术飞速发展。因为她不含有HTTP规范着置放的header,也未支持不过当一个新的资源要概念叫引入时才应该添加新URI的观——即版本不是表现形式的变迁。另一个反对之理由是资源URI是无见面随时间改变之,资源就是资源。

  URI应该能够简单地辨识资源——而不是其的“形状”(状态)。另一个即是要指定响应的格式(表征)。还有一对HTTP
headers:Accept 和 Content-Type。Accept
header允许客户端指定所期望或者能支持之应的传媒类型(一栽或多)。Content-Type
header可分别给客户端与服务端用来指定要或响应的多少格式。

  例如,要抱一个user的JSON格式的数量:

  #Request:

  GET http://api.example.com/users/12345
  Accept: application/json; version=1

  #Response:

  HTTP/1.1 200 OK
  Content-Type: application/json; version=1

  {“id”:”12345″, “name”:”Joe DiMaggio”}

  现在,我们对同资源要版本2底数码:

  #Request:

  GET http://api.example.com/users/12345
  Accept: application/json; version=2

  #Response:

  HTTP/1.1 200 OK
  Content-Type: application/json; version=2

  {“id”:”12345″, “firstName”:”Joe”, “lastName”:”DiMaggio”}

  Accept
header被用来代表所愿意的响应格式(以及示例中之版本号),注意上述两个一律之URI是哪就在不同之本子中分辨资源的。或者,如果客户端需要一个XML格式的数量,可以以Accept
header设置也”application/xml”,如果需要的话也足以带来一个点名的版本号。

  由于Accept
header可以为设置为允许多传媒类型,在应请求时,服务器将把响应的Content-Type
header设置为最般配配客户端请求内容之种。更多信息可以参见http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.Html

  例如:

  #Request

  GET http://api.example.com/users/12345

  Accept: application/json; version=1, application/xml; version=1

  上述呼吁中,假设服务器支持JSON
和XML格式的乞求,或者简单种植都支持,那么用出于服务器来控制最终回哪种档次的多少。但不论是服务器选择啊一样栽,都见面以响应中寓Content-Type
header。

  例如,如果服务器返回application/xml格式的数目,结果是:

  #Response

  HTTP/1.1 200 OK
  Content-Type: application/xml; version=1

  <user>
    <id>12345</id>
    <name>Joe DiMaggio</name>
  </user>

  为了求证Content-Type在发送数据给服务器时的用处,这里被闹一个就此JSON格式创建新用户的例证:

  #Request

  POST http://api.example.com/users
  Content-Type: application/json;version=1

  {“name”:”Marco Polo”}

  或者,调用版本2之接口:

  #Request

  POST http://api.example.com/users
  Content-Type: application/json;version=2

  {“firstName”:”Marco”, “lastName”:”Polo”}

当没有点名版本时,返回什么版本?

  并不需要在各国一个请求被都指定版本号。由于HTTP
content-negotiation(内容商)遵循类型的“最佳匹配”方式,所以若的API也当仍这一点。根据当时无异尺度,当客户端从未点名版本时,API应当返回所支撑之顶早版本。

  还是这例子,获取一个user的JSON格式的数额:

  #Request

  GET http://api.example.com/users/12345
  Accept: application/json

  #Response

  HTTP/1.1 200 OK
  Content-Type: application/json; version=1

  {“id”:”12345″, “name”:”Joe DiMaggio”}

  相应地,当因为POST方式向服务器发送数据常常,如果服务器支持多个例外版本,而求时又不曾点名版本,和地方的例证一样——服务器会将最为小/最早版本的数包含在body中。为了进行求证,下面的例子以JSON格式请求一个暗含多本资源的服务器,来创造一个新用户(预期会回来版本1):

  #Request

  POST http://api.example.com/users
  Content-Type: application/json

  {“name”:”Marco Polo”}

  #Response

  HTTP/1.1 201 OK
  Content-Type: application/json; version=1
  Location: http://api.example.com/users/12345

  {“id”:”12345″, “name”:”Marco Polo”}

呼吁不支持之本子

  当求一个休支持的本号时(包含在API生命周期中曾经不复存在的资源版本),API应当返回一个左的HTTP状态码406(表示不让奉)。此外,API还相应返回一个蕴含Content-Type:
application/json的响应体,其中蕴蓄一个JSON数组,用于证明该服务器支持的品类。

  #Request

  GET http://api.example.com/users/12345
  Content-Type: application/json; version=999

  #Response

  HTTP/1.1 406 NOT ACCEPTABLE 

  Content-Type: application/json

  [“application/json; version=1”, “application/json; version=2”,
“application/xml; version=1”, “application/xml; version=2”]

好家伙时该创建一个新本子?

  API开发被之多者都见面打破约定,并最后指向客户端有有不良影响。如果你免确定API的修改会带哪些的产物,保险起见最好考虑采用本控制。当你以考虑提供一个新本子是否得当时,或者考虑对现有的回表征进行改动是否定能满足急需并吃客户端所领时,有这样几独元素使考虑。

破坏性的改动

  • 变更属性名(例如将”name”改成为”firstName”)
  • 剔除属性
  • 改属性之数据类型(例如将numeric变为string,
    boolean变为bit/numeric,string 变为 datetime等等)
  • 改变验证规则
  • 于Atom样式的链接中,修改”rel”的价值
  • 每当存活的工作流中引入必要资源
  • 反资源的概念/意图;概念/意图或资源状态的义不同为其原本的意思。例如:
    • 一个content
      type是text/html的资源,之前表示的是具有支持之媒体类型的一个”links”集合,而初的text/html则代表的是用户输入的“web浏览器表单”。
    • 一个含有”endTime”参数的API,对资源”…/users/{id}/exams/{id}”表达的义是学员以大时刻付诸试卷,而初的意义则是考的预约了时。
  • 经过添加新的字段来转现有的资源。将片独资源统一为一个并弃用原来的资源。
    • 发生这般点滴单资源”…/users/{id}/dropboxBaskets/{id}/messages/{id}”和”…/users/{id}/dropboxBaskets/{id}/messages/{id}/readStatus”。新要求是将readStatus资源的属性放到单独的message资源中,并弃用readStatus资源。这将造成messages资源遭到指向readStatus资源的链接给移除。

  虽然上面列有底并无完美,但其深受起了一部分碰头指向客户端起破坏性影响之变类型,这时用考虑提供一个初资源要新本子。

非破坏性的修改

  • 于回来的JSON中补充加新属性
  • 加上指向任何资源的”link”
  • 添加content-type支持之初格式
  • 添加content-language支持的新格式
  • 出于API的缔造者和顾客都要拍卖不同的casing,因此casing的成形无关紧要

版本控制应在什么级别出现?

  建议针对单个的资源开展版本控制。对API的一部分转,如修改工作流,也许如果跨越多个资源的版本控制,以之来防范对客户端起破坏性的熏陶。

行使Content-Location来增进响应

  可选。见RDF(Resource Description Framework,即资源描述框架)规范。

带有Content-Type的链接

  Atom风格的链接支持”type”属性。提供足够的音信以便客户端好对一定的版及内容类型进行调用。

搜索来支持的本

自当以支持小只版本?

  维护多个不同之版本会吃工作转移得烦、复杂、容易失误,而且代价高,对于另外给定的资源,你应有支持非超越2只版本。

弃用

  Deprecated(弃用)的目的是因此来证实资源对API仍然可用,但在明天会面无在并转移得不可用。在意:弃用的时长将由弃用政策决定——这里连没有吃闹概念。

本人争告客户端给弃用的资源?

  许多客户端将来走访的资源或在新本子引入后会于废弃掉,因此,他们要来同等种方式来发现跟督察他们之应用程序对废弃用资源的使。当求一个弃用资源时,API应该正常应,并涵盖一个布尔种的自定义Header
“Deprecated”。以下用一个例证来进行认证。

  #Request

  GET http://api.example.com/users/12345
  Accept: application/json
  Content-Type: application/json; version=1

  #Response

  HTTP/1.1 200 OK
  Content-Type: application/json; version=1
  Deprecated: true
  {“id”:”12345”, “name”:”Joe DiMaggio”}

 

日期/时间处理

  如果无妥善地、一致地处理好日期及日来说,这将成一个雅累。我们经常会面遇到时区的题目,而且由于日期在JSON中凡是以字符串的格式在的,如果非指定统一之格式,那么解析日期呢会是一个题目。

  在接口内部,服务端应该坐UTC或GMT时间来存储、处理同缓存时间穿。这将实用缓解日期及时之题材。

Body内容被的日期/时间序列化

  有一个简的法子好化解这些问题——在字符串中尽用同一之格式,包括时间片(带有时区信息)。ISO8601时间格式是一个不易的化解方案,它用了了增强的年月格式,包括小时、分钟、秒和秒的小数部分(例如yyyy-MM-dd’T’HH:mm:ss.SSS’Z’)。建议在REST服务的body内容被(请求与应均包括)使用ISO8601代表有的日子格式。

  顺便取一下,对于那些基于JAVA的劳务以来,DateAdapterJ库使用DateAdapter,Iso8601TimepointAdapter和HttpHeaderTimestampAdapter类可以非常容易地剖析及格式化ISO8601日期以及时,以及HTTP
1.1
header(RFC1123)格式。可以打https://github.com/tfredrich/DateAdapterJ下载。

  对于那些创建基于浏览器的用户界面来说,ECMAScript5专业一开始就是隐含了JavaScript解析及创办ISO8601日期的情,所以其当改成我们所说的主流浏览器所遵循的章程。当然,如果你若支持那些不能自动解析日期的旧版浏览器,可以使用JavaStript库或正则表达式。这里产生几只可分析和创建ISO8601时间之JavaStript库:

  http://momentjs.com/

  http://www.datejs.com/

HTTP Headers中的日子/时间序列化

  然而上述提议才适用于HTTP请求或响应内容中之JSON和XML内容,HTTP规范对HTTP
headers使用另外一样栽不同的格式。在被RFC1123再度给之RFC822中指出,该格式包括了各种日期、时间跟date-time格式。不过,建议始终以时戳格式,在公的request
headers中它看起像这样:

  Sun, 06 Nov 1994 08:49:37 GMT

  不过,这种格式没有设想毫秒或者秒的十进制小数。Java的SimpleDataFormat的格式串是:”EEE,
dd MMM yyyy HH:mm:ss ‘GMT'”。

 

维护服务之平安

  Authentication(身份证明)指的凡肯定给定的请是从服务业已知道的某(或某系统)发出的,且请求者是外协调所声明的那个人。Authentication是为求证请求者的真实性身份,而authorization(授权)是为证实请求者有权力去履行为请的操作。

  本质上,这个历程是这般的:

  1. 客户端发起一个要,将authentication的token(身份证明令牌)包含在X-Authentication
    header中,或者将token外加在呼吁的查询串参数中。
  2. 服务器对authorization
    token(授权令牌)进行检讨,并进行说明(有效且非过),并基于令牌内容分析或者加载认证中心。
  3. 服务器调用授权服务,提供证明中心、被请资源与必备之操作许可。
  4. 只要授权通过了,服务器将会延续健康运行。

  上面第三步的付出可能会见于好,但是要是如果存在一个可是缓存的权能决定列表(ACL),那么在发远程请求前,可以在地头创建一个授权客户端来缓存最新的ACLs。

身份验证

  时极其好的做法是采用OAuth身份验证。强烈推荐OAuth2,不过它们仍然处于草案状态。或者选择OAuth1,它完全可胜任。在某些情况下吧得选取3-Legged
OAuth。更多关于OAuth的正统好查此http://oauth.net/documentation/spec/。

  OpenID是一个外加选择。不过建议以OpenID作为一个叠加的身份验证选项,以OAuth为主。更多关于OpenID的业内好查阅此http://openid.net/developers/specs/。

传输安全

  所有的印证都当使用SSL。OAuth2需要授权服务器和access
token(访问令牌)来行使TLS(安全传输层协议)。

  以HTTP和HTTPS之间切换会带来安全隐患,最好之做法是怀有简报默认都使用TLS。

授权

  对劳务的授权和指向另外应用程序的授权一样,没有外区别。它根据这样一个题材:“主体是不是对准加的资源产生要的许可?”这里被出了概括的老三码数据(主体,资源同认可),因此好轻构造一个支持这种概念的授权服务。其中重点是为给予资源访问许可的人还是体系。使用这些相似概念,就好啊各级一个主题构建一个缓存访问控制列表(ALC)。

应用程序安全

  对RESTful服务以来,开发一个平安的web应用适用同的法。

  • 于服务器上证实所有输入。接受“已知晓”的正确性的输入并拒绝错误的输入。
  • 防止SQL和NoSQL注入。
  • 利用library如微软的Anti-XSS或OWASP的AntiSammy来针对输出的数额开展编码。
  • 拿消息的尺寸限制在确定的字段长度内。
  • 劳应该就展示一般的错误信息。
  • 设想工作逻辑攻击。例如,攻击者可超过了多步骤的预订流程来预订产品而随便需输入信用卡信息呢?
  • 对可疑的活动记录日志。

  RESTful安全要注意的地方:

  • 证明数据的JSON和XML格式。
  • HTTP动词应该被拘在同意的道被。例如,GET请求不可知去一个实体。GET用来读取实体而DELETE用来删除实体。
  • 留神race
    conditions(竞争法——由于个别只或多独过程竞争下未克被以做客的资源,使得这些经过产生或因日子上推的次由一旦产出问题)。

  API网关可用于监视、限制及操纵对API的造访。以下内容可由网关或RESTful服务实现。

  • 蹲点API的施用情况,并问询哪些活动是正规的,哪些是非正常的。
  • 界定API的以,使恶意用户不能够止少一个API服务(DOS攻击),并且有力量阻止恶意之IP地址。
  • 以API密钥存储于加密底安密钥库中。

 

缓存和可伸缩性

  通过以网层级消除通过远程调用来博请求的数目,缓存提高了系统的不过扩展性。服务通过当响应中装置headers来增进缓存的能力。遗憾的是,HTTP
1.0饱受以及缓存相关的headers与HTTP
1.1不同,因此服务器如果又支持少数栽版本。下表给起了GET请求而支持缓存所须的最为少headers集合,并受来了适当的叙述。

HTTP Header

描述

示例

Date

一呼百应返回的日子及岁月(RFC1123格式)。

Date: Sun, 06 Nov 1994 08:49:37 GMT

Cache-Control

一呼百应可被缓存的太充分秒数(最充分age值)。如果响应不支持缓存,值也no-cache。

Cache-Control: 360

Cache-Control: no-cache

Expires

假使让出了最大age值,该时间戳(RFC1123格式)表示的是应过期的工夫,也不怕是Date(例如当前日子)加上最深age值。如果响应不支持缓存,该headers不有。

Expires: Sun, 06 Nov 1994 08:49:37 GMT

Pragma

当Cache-Control为no-cache时,该header的价为叫装置为no-cahche。否则,不有。

Pragma: no-cache

Last-Modified

资源本身最后吃修改的时空穿(RFC1123格式)。

Last-Modified: Sun, 06 Nov1994 08:49:37 GMT

  为了简化,这里选出一个应中之headers集合的例子。这是一个简单易行的针对资源进行GET请求的应,缓存时长为同样天(24时):

  Cache-Control: 86400
  Date: Wed, 29 Feb 2012 23:01:10 GMT
  Last-Modified: Mon, 28 Feb 2011 13:10:14 GMT
  Expires: Thu, 01 Mar 2012 23:01:10 GMT

  下面是一个类的例证,不过缓存被全然禁用:

  Cache-Control: no-cache
  Pragma: no-cache

ETag Header

  ETag
header对于证明缓存数据的初老程度很有因此,同时为促进条件的读取和更新操作(分别吗GET和PUT)。它的价值是一个任意字符串,用来代表回到数据的版本。不过,对于返回数据的不同格式,它为堪不同——JSON格式响应的ETag与同资源XML格式响应的ETag会不同。ETag
header的值好像带有格式的底层域对象的哈希表(例如Java中的Obeject.hashcode())一样简单。建议呢每个GET(读)操作返回一个ETag
header。另外,确保用双引号包含ETag的价值,例如:

  ETag: “686897696a7c876b7e”

 

HTTP状态码(前10)

  以下是出于RESTful服务还是API返回的极端常用之HTTP状态码,以及有关于她普遍用法的粗略说明。其它HTTP状态码不极端经常应用,它们或者更新鲜,要么更尖端。大多数劳动套件只支持这些常用的状态码,甚至只是支持中的一致部分,并且她都能健康干活。

  200 (OK) —— 通常的成状态。表示成功的太常见代码。

  201 (CREATED) ——(通过POST或PUT)创建成功。通过安装Location
header来含有一个对准最新创建的资源的链接。

  204 (NO CONTENT)
—— 封装了之应没有使用,或body中从来不其余内容经常(如DELETE),使用该状态。

  304 (NOT MODIFIED)
—— 用于产生规则的GET调用的响应,以减掉带宽的采取。
如果应用该状态,那么得也GET调用设置Date、Content-Location和ETag
headers。不分包响应体。

  400 (BAD REQUEST)
—— 用于履行要时或许引起无效状态的形似错误代码。如域名无效错误、数据丢失等。

  401 (UNAUTHORIZED)
—— 用于缺少认证token或说明token无效的错误代码。

  403 (FORBIDDEN)
—— 未授权的用户执行操作,没有权力访问资源,或者由某些原因资源不可用(如时间限制等),使用该错误码。

  404 (NOT FOUND)
—— 无论资源存不存,无论是否有401、403的限定,当呼吁的资源找不交常,出于安全因素考虑,服务器都得以应用该错误码来遮掩。

  409 (CONFLICT)
—— 每当执行要或会见招资源撞时常用。例如,存在双重的实业,当不支持级联删除时去根对象。

  500 (INTERNAL SERVER ERROR)
—— 当服务器抛来十分时,捕捉到的相似错误。

 

叠加资源

书籍

  REST API Design Rulebook,Mark Masse, 2011, O’Reilly Media, Inc.

  RESTful Web Services, Leonard Richardson and Sam Ruby, 2008,
O’Reilly Media, Inc.

*  RESTful Web Services Cookbook, Subbu Allamaraju, 2010, O’Reilly
Media, Inc.*

  REST in Practice: Hypermedia and Systems Architecture, Jim Webber,
et al., 2010, O’Reilly Media, Inc.

  APIs: A Strategy Guide, Daniel Jacobson; Greg Brail; Dan Woods,
2011, O’Reilly Media, Inc.

网站

  http://www.restapitutorial.com
http://www.toddfredrich.com
  http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm
  http://www.json.org/
https://github.com/tfredrich/DateAdapterJ
  http://openid.net/developers/specs/
  http://oauth.net/documentation/spec/
  http://www.json.org/JSONRequest.html
http://labs.omniti.com/labs/jsend
  http://enable-cors.org/
  http://www.odata.org/documentation/uri-conventions#FilterSystemQueryOption
  http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
  https://developer.linkedin.com/apis
  http://developers.facebook.com/docs/reference/api/
  https://dev.twitter.com/docs/api
http://momentjs.com/
  http://www.datejs.com/

 

于原本翻译的底蕴及经过改动:http://blog.csdn.net/huayuqa/article/details/62237010

英文原稿下载:RESTful Best Practices-v1
2.pdf

相关文章

网站地图xml地图