我想知道你是如何实现REST以下的用例。 它甚至有可能在不损害概念模型做什么?
阅读或单个事务的范围内更新多个资源。 例如,来自Bob的银行帐户转帐$ 100分成约翰的账户。
据我所知,实现这一点的唯一方法是通过作弊。 您可以张贴到任何与约翰或鲍勃相关联的资源,并使用一个单独的事务进行全部操作。 就我而言,这打破了REST架构,因为你基本上隧穿POST RPC调用,而不是个人的资源真正运行。
我想知道你是如何实现REST以下的用例。 它甚至有可能在不损害概念模型做什么?
阅读或单个事务的范围内更新多个资源。 例如,来自Bob的银行帐户转帐$ 100分成约翰的账户。
据我所知,实现这一点的唯一方法是通过作弊。 您可以张贴到任何与约翰或鲍勃相关联的资源,并使用一个单独的事务进行全部操作。 就我而言,这打破了REST架构,因为你基本上隧穿POST RPC调用,而不是个人的资源真正运行。
考虑REST风格的购物篮场景。 购物篮是概念上您的交易包装。 在可以添加多个项目到购物篮,然后提交篮子处理订单以同样的方式,你可以添加Bob的帐号进入交易包装,然后比尔的账户进入包装。 当所有的作品都在地方,那么你可以发布/所有的组分片段将事务包装。
但是也有一些不被这个问题,我认为这是太糟糕了回答了几个重要的情况下,因为它有搜索词在谷歌较高的排名:-)
具体来说,一个漂亮的propertly将是:如果您发布两次(因为有些缓存在中间打了个嗝),你不应该两次转账的金额。
为了得到这一点,您创建一个事务中的对象。 这可能包含所有你已经知道的数据,并把交易处于挂起状态。
POST /transfer/txn
{"source":"john's account", "destination":"bob's account", "amount":10}
{"id":"/transfer/txn/12345", "state":"pending", "source":...}
一旦你有了这个交易,就可以提交它,是这样的:
PUT /transfer/txn/12345
{"id":"/transfer/txn/12345", "state":"committed", ...}
{"id":"/transfer/txn/12345", "state":"committed", ...}
请注意,多放不在这一点无关紧要; 甚至在TXN一个GET将返回当前状态。 具体而言,第二PUT将检测到的首先是已处于合适的状态,只是返回它 - 或者,如果你试图把它变成了“rolledback”状态,它已经在“提交”状态后,你会得到一个误差,实际提交的事务了。
只要你跟一个单一的数据库,或者具有集成交易监控数据库,该机制将实际工作得很好。 你可能还引入超时的交易,你甚至可以用表达Expires头,如果你想。
在REST而言,资源是可以与CRUD(创建/读取/更新/删除)动词来作用于名词。 由于没有“转移资金”的动词,我们需要定义,可以在与CRUD采取行动“事务”资源。 下面是HTTP + POX的例子。 第一步是CREATE(HTTP POST方法)一个新的空事务:
POST /transaction
这将返回一个交易ID,例如“1234”,并根据URL“/交易/ 1234”。 需要注意的是射击这个帖子多次不会创造出多个ID相同的事务,也避免了引进“待定”状态。 此外,POST不能总是幂等(REST的要求),因此它通常是好做法,以尽量减少在帖子中的数据。
你可以留下一个事务ID的产生到客户端。 在这种情况下,你会POST /交易/ 1234创建事务“1234”,如果它已经存在的服务器会返回一个错误。 在错误响应,服务器可以返回当前未使用的ID与相应的URL。 这不是来查询与GET方法一个新的ID服务器是一个好主意,因为GET不应该改变服务器的状态,并创建/保留一个新的ID将改变服务器的状态。
接下来,我们更新 (PUT HTTP方法)交易的所有数据,隐含提交它:
PUT /transaction/1234
<transaction>
<from>/account/john</from>
<to>/account/bob</to>
<amount>100</amount>
</transaction>
如果与ID事务“1234”面前被提时,服务器对错误响应,否则OK响应和URL查看完成的交易。
注:在/帐号/约翰,“约翰”真的应该约翰的唯一帐号。
大的问题,剩下的就是大多与数据库般的例子,其中一些保存,更新,检索,删除解释。 有这样的一个,其中,服务器应该处理在某些方面的数据几个例子。 我不认为罗伊菲尔丁包括任何在他的论文,这是基于HTTP毕竟。
但他也谈“代表性状态传输”作为一个状态机,用移动到下一个状态的链接。 这样一来,该文件(陈述)保持,而不必做服务器跟踪客户端的状态。 这样一来,没有客户端的状态,只有状态无论在哪个链接你的。
我一直在思考这一点,在我看来是合理的获取服务器为你处理事情,当你上传时,服务器会自动创建相关的资源,并给你的链接,他们(事实上,它不会告发“T需要自动创建它们:它可能只是告诉你的链接,而当如果你跟随他们只创建它们 - 懒创造)。 同时还为您的链接创建新的相关资源-相关的资源具有相同的URI,但较长(加一个后缀)。 例如:
/transaction
毛刺会导致创建多个这样的资源,每一个不同的URI。 /transaction/1234/proposed
, /transaction/1234/committed
这类似于网页如何运作,最终的网页说:“你确定要这么做吗?” 这最后的网页本身的交易,其中包括一个链接转到下一个状态的状态的表示。 不只是金融交易; 也(例如)预览然后提交维基百科。 我想在REST的区别是,美国的序列中的每个阶段都有一个明确的名称(其URI)。
在现实生活中的交易/销售,经常有对交易的不同阶段(建议,购买订单,收据等)不同的物理文件。 更买房子,与结算等。
OTOH这感觉就像与语义,以我玩; 我跟动词转换成名词,使其REST风格的名词不舒服“因为它使用的名词(URI)来代替动词(RPC调用)”。 即名词“提交的事务资源”,而不是动词“提交该事务”。 我猜名词的一个好处是,你可以参考资源的名字,而不是需要在一些其他的方式将其指定(如维持会话状态,这样你就知道“这个”交易是什么...)
但最重要的问题是:什么是这种方法的好处? 即以何种方式是这样的REST风格比RPC方式更好? 是一种技术,是伟大的网页也可用于处理信息的帮助,以后保存/恢复/更新/删除? 我认为REST的主要优点是可扩展性; 的一个方面是不需要明确地维护客户的状态(但使得它在资源的URI,并作为其代表的联系下一个状态隐含的)。 在这个意义上它帮助。 也许这有助于分层/流水线呢? OTOH只有一个用户会看他们的具体事务,因此没有在缓存,以便其他人可以读取它,以大取胜的HTTP没有优势。
如果你站在回到这里总结讨论,这是很明显,REST是不适合大量的API,特别是当客户机 - 服务器交互本质上是有状态的,因为它是不平凡的交易。 为什么在所有的跳火圈建议,对于客户端和服务器,以迂腐遵循一定的原则,即不适合的问题? 一个更好的原则是给客户端的最简单,最自然的,有成效的方式与应用程序组成。
总之,如果你真的这样做在你的应用有很多交易的(类型,不是实例),你真的不应该创建一个RESTful API。
你必须推出自己的“事务ID”类型TX管理。 因此,这将是4个电话:
http://service/transaction (some sort of tx request)
http://service/bankaccount/bob (give tx id)
http://service/bankaccount/john (give tx id)
http://service/transaction (request to commit)
你不得不处理在DB的操作的存储(如负载平衡),或在内存或这样,那么处理提交,回滚,超时。
不是一个真正的RESTful的一天在公园。
我从这个话题渐行渐远了10年。 回来了,我简直不敢相信宗教伪装成你涉水只要在Google休息+可靠走进科学。 这种混乱是神话。
我会分裂这个广泛的问题分为三个:
您的需求就是一个根本。 不要让别人告诉你你的解决方案是不洁净。 法官有多好光的架构,以及如何简单地说,他们解决您的问题。
首先转账什么是你不能在一个单一的资源调用做。 你想要做的动作是寄钱。 所以你添加一个汇款资源给该帐户的发送者。
POST: accounts/alice, new Transfer {target:"BOB", abmount:100, currency:"CHF"}.
完成。 你不需要知道,这是一个必须是原子等你刚才汇款又名交易。 从A寄钱给B.
但对于这里的少数情况下,通用的解决方案:
如果你想要做涉及许多资源的东西很复杂,无法在规定范围内有很多的实际的交叉与什么障碍的原因(企业对实施知识)的限制,你需要转移状态。 因为作为客户端需要围绕转移状态REST应该是无状态的你。
如果传输状态,你需要从客户内部隐藏的信息。 客户不应该知道的内部信息仅由执行必要的,但不携带在业务方面的相关信息。 如果这些信息有没有商业价值的状态应该被加密和令牌一样的隐喻,通过什么需要使用。
这样一个可以通过内部状态和周围使用加密和签名,系统可仍然是安全和声音。 寻找合适的抽象为什么他绕过状态信息的客户端的东西是最多的设计和架构。
真正的解决方案:
记住REST在谈论HTTP和带有使用Cookie的概念。 当人们谈论REST API和工作流程和交互跨越多个资源或要求这些Cookie往往被人们遗忘。
还记得什么是写在维基百科有关的HTTP cookies:
饼干被设计为一个可靠的机制站点记住状态信息(如在购物车中的项目),或记录用户的浏览活动(包括点击特定的按钮,登录,或记录的页面被用户就访问早在几个月或几年前)。
所以基本上,如果你需要传递的状态,使用cookie。 它是专为完全同样的原因,它是HTTP,因此它是由设计:)到REST兼容。
更好的解决方案:
如果您谈谈执行涉及您平时所讲的协议的多个请求工作流客户端。 协议的任何形式的附带了每个潜在的一步一组前置条件一样执行步骤A之前,你可以做B.
这是自然的,但暴露协议的客户让一切更加复杂。 为了避免它只是认为,当我们需要做的在现实世界中复杂的相互作用和事情,我们做什么...。 我们使用一个代理。
使用代理的比喻,你可以提供一个可以执行所有必要的步骤,为您和存储实际分配/指示它在其列表在行事的资源(所以我们可以在代理或“机构”使用POST)。
一个复杂的例子:
买房子:
你需要证明你的可信度(如提供你的警方记录条目),你需要确保财务细节,你需要使用一个律师,一个可信的第三方存储的资金购买实际的房子,确认现在的房子属于你和购买的东西添加到您的纳税记录等。(只是作为一个例子,某些步骤可能是错误的或其他)。
这些步骤可能需要几天的时间才能完成,有的可以并行等进行
为了做到这一点,你只要给代理的任务,买房子这样的:
POST: agency.com/ { task: "buy house", target:"link:toHouse", credibilities:"IamMe"}.
完成。 该机构向您发送回一个参考给你,你可以用它来查看和跟踪此作业的状态,其余的由该机构的代理自动完成。
想想一个bug跟踪系统的实例。 基本上你报告错误,可以使用错误ID来检查怎么回事。 你甚至可以使用服务来听这个资源的变化。 任务完成。
我认为,在这种情况下,完全可以打破REST的纯理论在这种情况下。 在任何情况下,我不认为存在任何REST实际上是说,你不能在需要它的商业案例触摸依赖对象。
我真的认为这是不值得额外的篮球,你会通过跳跃来创建自定义事务管理器,当你可以只利用数据库来做到这一点。
你不能在REST使用服务器端的交易。
其中一个REST约束上的:
无状态
客户端 - 服务器通信是通过无客户端上下文被存储请求之间的服务器上进一步限制。 从任何客户端的每个请求包含了所有的服务请求所必需的信息,和任何会话状态中的客户端。
唯一的REST风格的方法是创建一个交易重做日志,并把它放到客户端状态。 随着请求的客户端发送的重做日志和服务器重新进行交易,
但也许它更简单使用基于服务器会话技术,支持服务器端的交易。
我相信这将是使用客户端上生成一个唯一的标识符,以确保连接打嗝不是由API保存的口是心非暗示的情况。
我认为使用生成的GUID领域的客户端传输对象一起,并确保同样的GUID是不会再重新插入会是一个简单的解决方案,以银行汇款的事。
不知道有更复杂的情况,如不同航空公司的机票预订或微架构。
我发现了一篇关于主题,相关的经验, 处理在RESTful服务的事务的原子 。
在简单情况下(不分配资源),你可以考虑的事务中的资源,在创造它的行为达到的最终目标。
所以,之间传输<url-base>/account/a
和<url-base>/account/b
,您可以张贴以下至<url-base>/transfer
。
<transfer> <from><url-base>/account/a</from> <to><url-base>/account/b</to> <amount>50</amount> </transfer>
这将产生一个新的传输的资源,并返回传输的新的URL -例如<url-base>/transfer/256
。
在成功后的片刻,然后,“真正的”交易进行的服务器上,且金额从一个帐户删除并添加到另一个。
然而,这并不包括分布式事务(如果说“一”一家银行保持一个落后的服务,和“b”背后的另一个服务另一家银行举行) - 只是说“尽力短语全部在不需要分布式事务的方式操作。”
我想你可以包括谭的URL /资源:
只是一个想法。