全文于2008年5月22日发布在InfoQ中文站上:http://www.infoq.com/cn/articles/tilkov-rest-doubts 并转载于2008年8月出版的《程序员》杂志上:http://www.cnki.com.cn/Article/CJFDTotal-ITSJ200808052.htm 摘要: 在了解过REST之后,你肯定很想知道这个概念在你的实际应用当中究竟能派上多大用场。而且,假如你已经熟悉另一套完全不同的架构手法的话,那么你担心“REST或REST式HTTP(RESTful HTTP),是否真的能在实践中派上用场,还是在介绍性的、‘Hello, World’级场景以外就不灵光了”是很正常的。我将在本文解答人们——尤其是那些深谙基于SOAP/WSDL的Web服务架构手法的人——开始研究REST时容易产生的关于REST的十点疑惑。 1. REST也许适用于CRUD,但并不适用于“真实的”业务逻辑 这是那些对REST的好处持怀疑态度的人最常见的反应。毕竟,要是你只能create/read/update/delete,那你如何表达更复杂的应用语义呢?我已经在本系列介绍性的文章中探讨过这些被大家所关心的问题了,不过这方面绝对值得进一步讨论。 首先,HTTP动词(verbs)——GET、PUT、POST和DELETE——跟数据库的CRUD操作并不是一一对应的。例如,POST和PUT都可用于创建新资源,它们的区别在于:PUT请求是由客户端决定(被创建或更新的)资源的URI;而POST请求是向一个“集合(collection)”或 “工厂(factory)”资源发出的,是由服务器来指派URI的。不过无论怎样,我们回到那个问题:如何应付更复杂的业务逻辑呢? <略> 2. 没有正式的契约与描述语言 从RPC到CORBA,从DCOM到Web服务,我们已习惯于拥有一个“列出操作、名称及输入输出参数类型”的接口描述(interface description)了。没有接口描述语言的话,REST怎么用呢? 就这一被十分频繁问到的问题,有三点答复。 首先,假如你决定用XML(这是很普遍的做法)来配合REST式HTTP的话,那么各种现有的XML模式语言(schema languages)(如DTD、XML Schema、RELAX NG、Schematron等) 仍旧可供你使用。可以说,一个用WSDL描述的东西,常常有95%的内容并不是跟WSDL相关、而是跟你定义的XML Schema复杂类型(complex types)相关的。WSDL所增加的,大部分是有关操作(operations)及其名称的——对于REST的统一接口(uniform interface)来说,描述这些是颇为无趣的,因为GET、PUT、POST和DELETE就是你所能使用的全部操作了。关于XML Schema的使用,这意味着,即便你依赖于一个REST式接口,你仍旧可使用你所偏爱的数据绑定工具(假如刚好你有的话)来为你偏爱的语言生成数据绑定代码。(回答还没结束,见下。) <略> 3. 谁真会把他们应用中如此多的实现细节暴露出来? 另一个普遍关心的问题是,资源太低层(low-level),暴露了那些不应暴露出来的实现细节。说到底,这不就把“按有意义的方式来运用资源”的担子加到客户端(消费者)的身上了吗? 简单的回答是:不。一个资源的GET、PUT或其他方法的实现,可以跟一个“服务”或RPC操作的实现复杂程度相当。应用REST设计原则,并不是说你必须把下层数据模型(underlying data model)中的各项暴露出来——它只意味着,你采用以数据为中心的(data-centric)方式、而不是以操作为中心的(operation-centric)方式把业务逻辑暴露出来。 <略> 4. REST只能配合HTTP使用,它不是传输协议无关的 首先,毫无疑问,HTTP不是一种传输协议(transport protocol),而是一种应用协议(application protocol)。它采用TCP作为下层传输(underlying transport),但它拥有自己的语义(否则它就没什么用处了)。仅将HTTP作为传输,是不恰当的。 <略> 5. 没有实际的、明确且一致的指南教你如何设计REST式应用 REST式设计在许多方面均没有“官方”最佳实践和“如何按符合REST原则的方式、用HTTP解决一个特定问题”的标准方式。毋庸置疑,这是会逐渐得到改善的。尽管如此,REST具体表达了比基于WSDL/SOAP的Web服务更多的应用概念。换言之,虽然该批评对REST有很大价值,但这一批评更适用于其替换技术(它们基本上没有向你提供任何建议)。 <略> 6. REST不支持事务 “事务(transaction)”一词存在着多种不同解释,不过人们一般所说的事务,指的是数据库里的ACID这种。在一个SOA环境中——无论是否基于Web服务或HTTP——各个服务(或系统、或Web应用)的实现仍然有可能与一个支持事务的数据库进行交互:这无需很大改变,假如你不用显式创建事务的话(除非你的服务运行在一个EJB容器或其他可以为你处理事务创建的环境中)。如果你与多个资源交互,情况也一样。 <略> 7. REST是不可靠的 常有人指出,REST式HTTP里没有与WS-ReliableMessaging对等的特性,于是许多人便断定,REST不能应用于讲究可靠性(reliability)的场合(那就是说差不多所有跟业务场景相关的系统)。但很多时候,你不一定需要一个处理消息递送(message delivery)的基础设施组件(infrastructure component),相反,你需要知道一个消息是否已被递送。 <略> 8. 不支持发布/订阅 本质上,REST基于的是一种客户端-服务器模型(client-server model),HTTP总把客户端和服务器称为通信端点(endpoints of communication)。客户端通过发送请求和接受响应的方式与服务器进行交互。在发布/订阅模型(publish/subscribe model)中,客户订阅特定种类的信息,然后每当有新信息出现时它就会得到通知。REST式HTTP环境怎么可能支持发布/订阅呢? <略> 9. 无异步交互 在HTTP的请求/响应模型之下,如何实现异步通信?同样地,我们应注意到人们在谈及异步性(asynchronicity)时常常指的是不同方面。有人指的是编程模型,它可以是跟线上交互(wire interactions)无关的阻塞或非阻塞模型。这不是我们所关心的。但假如你把一个请求从客户端(用户)递送到服务器端(提供者)的过程需花费数小时,这怎么办呢?用户如何知道处理有没有结束? <略> 10. 缺少工具 最后一点,人们常常抱怨缺少用于支持REST式HTTP开发的工具。正如我在第二点里提到的,在数据方面其实不是这样——你可以使用你熟悉的数据绑定与其他数据APIs,因为这与方法数量和调用它们的方式无关。至于普通的HTTP与URI支持,现在所有的编程语言、框架及工具包都能提供立即支持。最后,厂商们正在为“用它们的框架进行更便捷的REST式HTTP开发”提供越来越多的支持,例如Sun的JAX-RS(JSR 311)、微软的.NET 3.5及ADO.NET数据服务框架里对REST的支持。 <略> 总结 那么,REST及其最常见的实现——HTTP——理想吗?当然不。世界上没有在所有情况下都理想的东西,而且很多时候即便在单个情况下都未必能够理想。我已经避免了许多相当合理、但需要更复杂解答的问题领域,比如基于消息的安全、部分更新以及批处理等,我承诺将在后续文章中讨论这些问题。希望我已经解答了你的一些疑惑——假如我遗漏了你最关心的问题,欢迎在此发表评论。