在过去的这几年当中,当人们想要构建一个 HTTP API,在诸如 XML-RPC、SOAP以及 JSON-RPC 这些选项之中,几乎都会选择 REST 作为首选的架构风格。REST 的出现最终被认为优于其它的“基于 RPC”的方式,这其实是一种无解,它们只是不同而已。 本文讨论构建 HTTP API 的场景中的两种方法, 因为这两种方法最常被用到。REST 和 RPC 都可以被其他的传输协议使用,比如 AMQP,不过那完全是另外一个话题了。 REST 表示的是“描述性状态传递(representational state transfer),” Roy Fielding 在他的论文做了如是描述。可悲的是,那篇论文并没有多少人读过,许多人对 REST 是什么都有自己的简介,这就导致许多的混乱和分歧。REST 整个就是关于 客户端和服务端之间的关系的,其中服务端要提供格式简单的描述性数据,常用的是 JSON 和 XML。对这些资源或者资源集合的描述,有些也许是可以通过一种叫做超媒体的方法所发现的动作和关系来修改的。 超媒体是 REST 的基础,它本质上就是一个向其它资源提供链接的概念。 |
除了超媒体之外,还有其它的一些约束,如下:
这些约束 (还有另外一些) 让 REST 架构能帮助 API 持续使用几十年,而不是几年。 在 REST 流行 (在诸如 Twitter 和 Facebook 这样的公司将它们的 API 称作 REST 以后)之前, 大多数 API 都是使用 XML-RPC 或者 SOAP 构建的。XML-RPC 是有毛病的,因为要确定 XML 的数据类型,开销是比较大的。在 XML 中,许多东西都只是字符串而已,因此你需要在顶部有一层元数据来描述诸如那一个域对应哪一种数据,之类的事情。这成为SOAP(简单对象访问协议 Simple Object Access Protocol)基本的组成部分。XML-RPC 和 SOAP, 以及自定义的本土解决方案主宰了 API 领域很长一段时间,而它们都是基于 RPC 的 HTTP API。 |
“RPC”指的是“远程过程调用(remote procedure call)” ,本质上在JavaScript、PHP、Python等等中调用都是一样的:取方法名,传参数。因为不是每个人都喜欢XML,RPC API可以使用 JSON-RPC协议,也可以考虑自定义基于JSON的API,像Slack 是用它的Web API实现的。 以这个RPC调用为例:
JavaScript也是如此,定义一个函数,然后在其他地方调用:
想法一样,定义公共方法建立API接口,然后这些方法传入参数被调用。RPC就是一堆功能,但在一个HTTP API的上下文里,需要把方法放在URL中,同时将参数放在查询串或请求块(body)中。SOAP访问一个大同小异的数据时,就像报告一样,异常冗长。如果你在Google中搜索“SOAP案例”,你会找到下面类似,名为getAdUnitsByStatement的案例:
这样的数据载荷相当庞大,如包装为参数仅仅就一行:
在JavaScript中, 看起来会是这样:
JSON API会更加简洁, 或许看起来像这样:
|
尽管此负载更简单,但我们还是需要不同的方法来处理getAdUnitsByStatement 、 getAdUnitsBySomethingElse。当你看这样的案例时,REST很快就能适应,因为它允许泛型端点与查询字符( 例如,GET /ads?statement={foo}orGET /ads?something={bar})联合查询。你可以联合字符去获取GET /ads?statement={foo}&limit=500,然后就可以考虑抛弃以前看似SQL语法的那种参数了。 目前,REST看似更好,但只是因为这些使用RPC处理的服务是REST更擅长处理的。这篇文章将不再尝试阐述什么”更好“,但取而代之的是帮你做出非正式的决定,以决定一个方案何时更合适。 |
它们是什么?
|
1
2
3
4
|
POST /SendUserMessage HTTP/ 1.1 Host: api.example.com Content-Type: application/json { "userId" : 501 , "message" : "Hello!" } |
而在 REST 方法中,做同样的事情会这样:
1
2
3
4
|
POST /users/ 501 /messages HTTP/ 1.1 Host: api.example.com Content-Type: application/json { "message" : "Hello!" } |
虽然它们看起来很相似,但这里确实有一个概念上的区别:
-
RPC
发送一个消息,然后可能会往数据库里保存一些东西以作为历史记录,有可能另一个 RPC 会在这里请求同一个数据——谁知道呢?
-
REST
在用户消息集中创建一个消息资源,如果用同一个 URL 的 GET 请求,可以看到之前的用户消息,消息将在后台发送。
这里 “事后发生的行为” 在 REST 可以干很多事情。想像一个拥有“旅途”的拼车App。那些旅途需要“开始”、“结束”和“取消”动作,否则用户就无法知道它们何时开始,何时结束。 使用 REST API,你已经拥有 GET /trips 和 POST /trips,然后很多人会尝试使用有点像下面这些动作的节点:
这些节点基本上就是在 REST API 中混入了 RPC 风格,这是流行的解决方案,但是从技术上来说并不是 REST。这个现象展示了将行为放在 REST 中的难处——看起来并不明显,但却是可能的。有一种方法是使用状态机,比如使用 state 字段:
像其它字段一样,你可以给 status PATCH 一个新值,在后台启动一些重要的行为:
Statesman 是 Ruby 中一个简单的状态机,它由 GoCardless 团队开发。有各种不同语言的许多状态机实现,但对于用作演示来说,它是最简单的一个。 基本上在你的控制器中,libcode 或 DDD 逻辑的某处,你可以在 PATCH 请求中找到“status”,然后,可以尝试转变这个状态:
这段代码执行的时候,它会进行转变,并且运行定义在 after_transitionblock 中的任何逻辑。如果不成功,就抛出错误。 成功的动作可能是任何东西:发送邮件、推荐提醒、调用另一个服务以开始监控驾驶员的 GPS 所代表的汽车位置——什么都行,只要你喜欢。 RPC 方法 POST /startTrip 或者像 REST 的 POST /trips/123/start 节点都不再需要,因为只需要简单的处理就可以遵循 REST API 的约定。 |
当行为不事后发生时我们已经看到了两种适应REST API行为,而不破坏其RESTful的方法,但是依赖于API被构建的应用程序的不同,这些方法可能会让人觉得越来越缺少逻辑,像在转圈圈。有人开始怀疑,为什么我要试图将所有的行为都使用REST API来实现?RPC API或许能对现有的REST API进行更好地补充。我们可以相对放宽使用RPC的Web API的条件,因为它只用于不适合使用REST API的情况。设想一下,我们为用户提供“tick”,“ban”或者“leave”选项,让用户只使用REST进行保留操作,或者从一个频道或者整个Slack中进行移除操作,如下:
刚开始,DELETE似乎最适用的HTTP方法,但是这个需求却相对模糊。它可能意味着完全关闭用户账户,也可能只是禁用这个用户。可以肯定的是,它绝对是这些选项中的一个。另一种方法是尝试使用PATCH:
有一点很奇怪,因为用户的状态不会因为任何原因被kick,这就需要获取进一步的传递信息进入指定频道。
这样处理仍然不和常理,因为它产生了一个新的专用字段,并且这个字段实际上并不是为用户而存在的。我们尝试使用如下关系:
这样好一些了,因为我们不会混淆全局/users/jerkfase的资源,但依然缺少“kick”,“ban”或者“leave”选项。再次将它们放入正文或者查询语句中,为RPC方式添加一个专用字段。 |
||||||||
唯一想到的其他方法是创建一个kicks集合,一个bans集合和一个leaves集合,另外为POST/kicks,POST/bans和POST/leaves端点创建一些端点用于匹配。这些集合将允许特定资源的元数据,例如,列出用户被踢出的频道,但是感觉很像强制将一个不适合的应用程序作为范例。 Slack的网页API看起来像下面这样:
简单漂亮!我们仅仅为手头的任务发送了参数,就像任何具有函数的编程语言中一样。 一个简单的经验法则:
如果两者都不是明显的赢家?你选择哪种方法? 同时使用REST和RPC你需要选择一个方法并只有一个API的想法是错误的。应用程序可以很轻松的拥有多个不被视为“主”API的API或者额外的服务。使用暴漏HTTP端点的API或者服务,你可以选择遵循REST或者RPC规则,只需要有一个REST API和少量的RPC服务。例如,在会议上,有人问这个问题:
除了创建一个具有POST/restartServer方法和POST/execServer方法的简单的RPC风格的服务,并可以通过REST服务器构建和维护的服务器上执行的服务之外,没有什么可行的方法时可行的。 |
总结
|
本文标题:理解面向 HTTP API 的 REST 和 RPC
本文地址:https://www.oschina.net/translate/understanding-rest-and-rpc-for-http-apis
参与翻译:imqipan, 边城, leoxu, wnull, Tocy
英文原文:Understanding REST And RPC For HTTP APIs