REST架构风格是否需要物理上分离的客户端和服务器?

3

RESTful交互是否需要在物理上分离的客户端和服务器之间进行?即交互是否需要以某种方式涉及网络栈?在应用程序的各个组件之间采用类似HTTP的“调用约定”是否有益?

对我来说,REST的好处几乎与在同一应用程序的组件之间通信一样适用于在物理上分离的客户端和服务器之间通信,并且可以满足REST的约束和指导原则,而无需涉及网络栈。(我并不是建议每个函数调用都“看起来像HTTP”,但对于某些函数调用或交互,使用类似HTTP的交互可能是有意义的。)

例如,在Web应用程序中,通过像http://api.local/user/34这样的URL访问(“内部”)用户信息可能很有用,并且使用“HTTP客户端”在内部路由和分派请求,而不是通过标准的HTTP路由和分派过程。开发人员不需要提供传统的库和相关文档,而是提供URL终端点(资源),可以使用标准的HTTP动词进行操作。由于开发人员已经熟悉HTTP,所以需要的文档较少,并且在组件之间会有更多的一致性和统一性。
我认为,以这种方式思考REST也有助于澄清REST和SOAP等RPC机制之间的区别--没有理由使用SOAP进行“内部”调用,但REST的已知行为和语义可能使其在某些情况下对“内部”调用有用。当然,如果您正在使用REST进行内部调用(REST Level 0?),则将此类交互转换为外部调用非常简单,如果需要,可以这样做。
3个回答

1
REST 的思想在很大程度上受到数据传输、远程性和架构中立性的经济学驱动。访问远程资源是昂贵的;你需要一种鼓励可缓存性、可寻址性和极简语义的体系结构。然而,对于进程内通信,在子系统之间发送数据非常廉价,并且通常等同于传递指针,这满足或取消了所有三个目标。
我承认我没有深入思考过这个问题,所以回答问题可能是有必要的:进程内 HTTP 如何使我的生活更加轻松?

回答你的问题,我会说在进程中使用REST可以让你的生活更轻松,因为,除了其他原因外:(a) RESTful交互是众所周知的,并且在很大程度上是自我记录的;(b) 如果必要,它使将服务从满足于进程中到远程变得微不足道。我认为“可寻址性”和“可缓存性”以及其优点同样适用于进程内REST和外部REST。(例如,在内部使用memcache非常普遍--如果可以,为什么不使其参与透明呢?) - mjs
@mjs:点(a)不太令人信服。函数调用也是众所周知的,甚至比REST更为熟悉。点(b)很有趣;如果您将来可能会远程操作(或想要混合使用),我可以看到它会很有用。我不同意缓存评论,因为如果值得并且透明地缓存结果,函数可以缓存结果,并且在向调用者公开HTTP样式的缓存语义方面几乎没有收益。缓存可能有所帮助的一个领域是数据库API,但这不再是相同的场景(也不是memcache)。 - Marcelo Cantos
@mjs:只是为了澄清一点:HTTP缓存不利于进程内客户端-服务器交互的原因在于,一旦函数决定缓存结果,无论调用者重复获取相同数据多少次,该函数都只是返回相同指针,成本微乎其微。在传统的HTTP/REST交互中,客户端缓存会产生很大的影响。 - Marcelo Cantos
也许我没有理解你的意思,但我不明白为什么客户端 HTTP 风格的缓存基本上比客户端 memcached 缓存更有用。当然,它不能让你避免网络往返(因为没有涉及网络堆栈),但是 HTTP 的缓存控制头的复杂性(客户端和服务器都可以控制缓存何时以及如何使用)在某些情况下似乎很有用。而且它们也是明确定义的 - 如果你了解 HTTP,你已经知道“服务器”和“客户端”应该如何行事。 - mjs
@mjs:也许我的措辞不够清晰。我的意思是,当客户端和服务器在同一个进程中时(这就是我们讨论的上下文),HTTP风格的缓存和memcache根本没有用处。您可以将缓存语义委托给充当“服务”的函数的实现细节。事实上,这是一种众所周知的技术,称为记忆化(或更一般地说,动态规划)。 - Marcelo Cantos
显示剩余2条评论

1

在进程中使用REST原则和HTTP语义确实是有意义的,但只有当您的应用程序最终也是一个与HTTP通信的客户端或服务器时才有意义。

困难的部分是遵守HTTP的分层约束,因为很容易调用该层另一侧的单例,因为它只是一个函数调用而已。

然而,其中一个好处是您实际上可以将一层从一个位置移动到另一个位置。这可能很难完全实现,但我认为这是可行的,尽管我猜想这从未被实现过。

在我的思想实验中,HTTP的所有好处都发挥了作用,以一种纯粹的内存缓存或进程内缓存无法处理的方式。例如,考虑缓存验证或条件放置。想象一下能够进行具有HTTP请求表达力的函数调用:

从此服务检索此内容,但仅当其ETag不是“A”或“B”或W /“C”时,因为这些是我目前拥有的内容

或者

把这个存到这里,但只有在ETag W /“DEF”仍然有效的情况下才能存储,因为这是我刚刚执行GET时使用的标记。

我想要搜索像这样的小部件,并且最好将结果作为IAtomCollection返回,但我会接受List (Accept)。上次我问这个问题时,我得到了一个ETag“foo” (If-None-Match),所以如果它没有改变,我就不需要它,但如果您不介意,我想验证我的缓存与源服务器的有效性 (Cache-Control: must-revalidate)。哦,顺便说一下,这是我的凭据 (Authorization)。

像这样的问题在HTTP中非常容易解决,我们都知道如何伪造这样复杂的查询。

对于HTTP响应也是如此:

嗨,我发现了你的IAtomCollection,并且我确实通过原始服务器进行了验证。你现在拥有的东西已经不再有效了,所以这里有一个新的给你。它具有“bar”的ETag,你可以在两分钟内将其视为新鲜的而无需重新验证,但如果下次询问时我不在,你可以将新鲜期延长一分钟。当然,响应会根据你的凭据和偏好而变化(这是不言而喻的,对吧?)。
再次强调,这只是普通的HTTP。想象一下能够进行如此智能的方法调用!
至于HATEOAS,将一些思考放入封装标识符中将使得遵守该约束成为可能。但是我的思维实验在这个方向上并没有走得很远...

0
在强调资源而不是其背后的功能的RESTful系统中,资源的URI通常是访问该资源的最自然方式。这就是资源所知道的,为什么不使用它呢?
在某些系统中,不总是清楚要执行哪些函数调用以获取由资源提供的数据。同样,通过其URI(甚至从您的代码内部)简单地寻址资源可能是最方便的。
RESTx中,我们为此提供了一种非常简单的方法,使您作为组件作者能够访问托管在此服务器上的资源的数据。同时,这种抽象还使得可以使用完全相同的语法引用托管在其他服务器上的数据。但是,您需要调用accessCode(<uri>)方法,而不是手动进行HTTP请求。当然,如果URI引用本地资源,则不会有实际的HTTP请求,但这是您无需担心的事情。以下是一个示例

当然,在您的代码中不必使用硬编码的URI。RESTx是关于可重用代码的,根据需要进行配置以创建新的RESTful资源。因此,您所引用的URI通常是组件配置的一部分。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接