我对 Roy Fielding 的 REST 替代 HTTP cookie 的解释是不是正确?

Posted

技术标签:

【中文标题】我对 Roy Fielding 的 REST 替代 HTTP cookie 的解释是不是正确?【英文标题】:Is my interpretation of Roy Fielding’s REST alternative to HTTP cookies correct?我对 Roy Fielding 的 REST 替代 HTTP cookie 的解释是否正确? 【发布时间】:2021-08-11 18:42:10 【问题描述】:

根据 Roy Fielding 的博士论文 Architectural Styles and the Design of Network-Based Software Architectures,HTTP cookie 违反 REST 架构风格,因为它们独立于应用程序状态并且没有语义,@987654321 @:

对协议进行了不适当的扩展以支持与通用接口的所需属性相矛盾的功能的一个示例是以 HTTP cookie 的形式引入站点范围的状态信息。 Cookie 交互无法匹配 REST 的应用程序状态模型,通常会导致典型浏览器应用程序的混乱。

Cookie 也违反了 REST,因为它们允许在没有充分识别其语义的情况下传递数据,从而成为安全和隐私方面的问题。 cookie 与 Referer [sic] 标头字段的组合使得在用户浏览网站时跟踪他们成为可能。

因此他提出了以下替代方案:

因此,Web 上基于 cookie 的应用程序永远不会可靠。应该通过匿名身份验证和真正的客户端状态来完成相同的功能。使用上下文设置 URI 而不是 cookie,明智地使用上下文设置 URI 可以更有效地实现涉及偏好的状态机制,其中明智意味着每个状态一个 URI,而不是由于嵌入用户 ID 而导致的无限数量的 URI。同样,通过在超媒体数据格式中定义购物项目的语义,允许用户代理选择和存储这些项目,可以更有效地实现使用 cookie 在服务器端数据库中识别用户特定的“购物篮”在他们自己的客户端购物篮中,并带有一个 URI,用于在客户准备购买时结帐。

我对他的用户偏好示例的理解如下。假设一个网站允许其用户在 URI /preferences(如 Stack Overflow)的首选项页面中选择浅色主题(默认)和深色主题。当用户选择深色主题时,他应该被重定向到 URI /preferences?theme=dark,其 html 表示形式将与 URI /preferences 的 HTML 表示形式相同,只是现在处于深色模式和查询 @987654327 @ 将附加到所有嵌入的超链接。这样,如果用户选择例如在 URI /home?theme=dark(不是/home)的主页的嵌入式超链接,那么主页的 HTML 表示也将处于暗模式,查询 ?theme=dark 也将附加到其所有嵌入的超链接。要恢复到轻主题,然后用户选择在 URI /preferences?theme=dark 处的首选项页面的嵌入超链接,在首选项页面中选择轻主题并应重定向到 URI /preferences,其 HTML 表示将与URI /preferences?theme=dark 的 HTML 表示,除了它现在将处于轻量模式并且查询 ?theme=dark 将从所有嵌入的超链接中删除。这是罗伊菲尔丁的意思吗?

对于他的购物车示例,当用户将产品 i 添加到购物车时,他应该被重定向到带有查询 ?product-i=product-i&quantity-i=quantity-i 的 URI,其 HTML 表示将把该查询附加到所有其嵌入的超链接。这样,当用户选择结帐超链接/checkout?product-1=product-1&quantity-1=quantity-1&…&product-n=product-n&quantity-n=quantity-n时,购物车的内容就会发送到网站。这是罗伊菲尔丁的意思吗?

【问题讨论】:

我没有读过菲尔丁的论文,也没有遵循那些引文中的推理,但是既然你(带外)让我看这个问题,我会说在我看来,主题之类的东西听起来像是两种不同的表示,例如机器可读 API 中的 JSON 与 XML。这些通常使用 HTTP 的内容协商协议而不是不同的 URL 来处理。毕竟,它们是同一资源的两种不同表示形式。 @MarkSeemann 感谢您的反馈。 content negotiation 的问题在于用户无法在 Web 浏览器中控制它。以及如何通过内容协商将用户选择保留在后续网页中? 我不确定我是否遵循。 REST 是一种机器可读 API 的设计理念。它们不应该被浏览器使用。例如,您不能使用浏览器发出 PUTDELETE 请求。 @MarkSeemann REST 实际上并不局限于机器可读的 API:'对我来说更重要的是相同的设计反映了良好的人类 Web 设计,因此我们可以设计协议来支持机器和','它需要知道的只是这些指令的含义以及它下一步想要做什么的一些想法,无论该目的是用户驱动的、配置驱动的还是某些有点人工智能驱动。'(参见 Roy Fielding 的 cmets here)。 @MarkSeemann 您实际上可以使用 javascript 对象 XMLHttpRequest 在浏览器中发送 PUTDELETE HTTP 请求。 【参考方案1】:

我相信你在第一种情况下正确解释了菲尔丁的论文,但不是第二种情况。

您对“首选项”的解释似乎完全正确:拥有多个资源,其表示包含相同的信息但不同的表示是完全合理的,例如将深色主题和浅色主题作为并行资源结构。

但是我相信你误解了菲尔丁的“客户端购物篮”的提议。他并不是提议引入要编辑的服务器端资源(毕竟,这种能力已经存在于我们今天的网络中);而是引入一种通用语言来存储客户端状态的有趣片段在客户端

换句话说,Fielding 正在讨论在 HTML 标准中引入一些控件(类似于 Web 表单的控件),这些控件将允许人们保存一些信息,然后在实际下订单时将其加载到表单中.

想象一下,如果你愿意,一种特殊的表单在提交时会编辑 Web 浏览器本身本地的资源。因此,您可以从目录中挑选商品,这样您的本地购物车资源就会被修改。当您准备结账时,您的购物车中的内容就可以发送到服务器了。

就像表单是通用的一样,可以用于许多不同的领域,所以我们希望这个购物车管道是通用的,这样它就可以用于任何类型的“复制此信息以后使用”机制。

诀窍(没有发生)是定义一个标准,然后让每个人(浏览器制造商)以足够相似的方式实施这些标准,让一切正常运行。

【讨论】:

感谢您的反馈。关于购物车的例子,我也想过你的另类解释。这已经可以通过 HTML5 中的 History API 实现:您只需从 Javascript 调用 window.history.pushState(data, '', uri) 以创建一个新的应用程序状态,将购物车 data 存储在浏览器的应用程序历史记录中,并可选择更改 uri,然后您可以使用window.history.state 检索data,并使用POST 请求的正文将其发送到结帐URI。是你的意思吗? 这种方法的缺点是,如果在浏览器中禁用了 Javascript,它就不起作用,因此如果您希望它在没有 Javascript 的情况下工作,则必须将购物车数据存储在 URI 中,即与用户偏好示例的方法基本相同。 哦,对不起,我现在明白你的意思了:菲尔丁建议的重点是允许通过响应表示的超媒体格式本地操作应用程序状态(例如 HTML 中的新 <shoppingitem> 元素将点击转换为应用程序状态的更新),而不是依赖于客户端脚本(例如 JavaScript 的 History API)。 关于用户偏好示例,为了确定,我的解释是,为了在多个操作中携带存储在 URI 中的用户偏好,每个资源都应该有一个表示,其中包含存储相同用户偏好的超链接而不是资源 URI(例如,资源/foo?x=bar 表示中的所有超链接都应具有查询组件x=bar,以便可以传播信息x=bar)。你同意这种解释吗?【参考方案2】:

正如我在上面的 cmets 中所说的,我没有阅读菲尔丁的论文,但我一直在思考这个问题,并决定写下我的想法。

我认为不需要阅读论文来理解 REST 设计。我这样说并不是要贬低菲尔丁的工作,这显然是非常有影响力的。另一方面,论文是从2000年开始的,不能有太多的实践经验。 2000 年我还是一名初级开发人员,相信我,REST 不是一回事。如果您完全从事 Web 服务,那么 SOAP 就在其中。

我主要从 Subbu Allamaraju 的 RESTful Web Services Cookbook 以及 Ian Robinson、Jim Webber 和 Savas Parastatidis 的 REST in Practice 中学习了 REST。在我看来,这些书所基于的开发生产服务的实践经验比菲尔丁在撰写论文时可能拥有的经验要多。

说了这么多,我想我可以解析菲尔丁关于购物篮的报价:

在超媒体数据格式中定义购物项目的语义,允许用户代理在他们自己的客户端购物篮中选择和存储这些项目,并在客户端准备好时提供用于结账的 URI购买

注意它谈到了一个客户端购物篮。这意味着客户有责任跟踪状态。

我对此的解释是,每个购物项目都是一个单独的资源,这几乎没有争议。在 REST 中,资源由地址标识,因此购物篮只是 URL 的集合:

/products/12345
/products/56789
/products/90125

客户端必须像上面的列表一样维护资源地址列表。一旦它准备好签出,它会POST 到为此目的而提供给它的 URI 的列表:

POST /check-out HTTP/1.1
Content-Type: text/plain
/products/12345
/products/56789
/products/90125

在这里,我刚刚使用了一个以换行符分隔的纯文本列表,但也可以将数据编码为 XML 或 JSON(后者在 2000 年也不是真正的东西)。

我不认为我会像那样设计一个 RESTful 购物篮,但这就是我对上面引用的解释。

【讨论】:

"论文是2000年的,不能有太多的实践经验。" - 我想看起来是这样,但是,这是由于不幸的事实是该行业已将名称盗用为不同的概念。 Fielding 的 REST 和行业 REST 确实是两个不同的东西。这篇论文根本不是关于(基于 RPC 的)Web 服务的设计,它有点抽象地描述了他所说的“互联网规模的分布式超媒体系统”的架构约束和属性,真的万维网(当时已有 10 多年的历史)。 @FilipMilovanović 这是公平的批评。在我写的时候,我还没有读过论文。我与自己争论我是否应该回答这个问题,但 OP 要求我(带外)考虑这个问题,基于我在这个词的现代解释中对 REST API 所做的其他工作。我可能误解了意图,以至于我认为这也是这里的上下文。如果那是错误的,我很抱歉。 感谢您分享您的解读马克。是的,我对它很感兴趣,因为您已经阅读了 RESTful Web Services Cookbook,而我没有。您的建议似乎是 Roy Fielding 所建议的(除了他还指定将用户点击产品转换为将其 URI 添加到本地购物车中应该可以通过对超媒体格式的理解而不是依赖在浏览器中的 Javascript 等客户端脚本上)。顺便说一句,在购物车中,我们还需要数量信息,所以你会添加重复的 URI 还是数量字段? @Maggyero 实际格式有待协商。您可以添加重复的 URL,或者添加数量列或属性,具体取决于格式。但正如我所写,我可能不会设计这样的购物篮;我认为购物篮资源可能更有意义,但归根结底,这取决于您要解决的具体问题。 @Maggyero 是的,这是一种方法。您还可以为每个用户拥有一个购物车,而不是创建新的购物车:/users/1234/cart,这样就无需先创建资源。这取决于您希望系统如何工作,这(也是)一个业务决策,以及一个技术问题。

以上是关于我对 Roy Fielding 的 REST 替代 HTTP cookie 的解释是不是正确?的主要内容,如果未能解决你的问题,请参考以下文章

REST 的无状态原则究竟意味着啥?

理解RESTful架构

胡说REST(REpresentational State Transfer)

实习第一周学习总结

Javascript ROY G BIV 线谱?

理解本真的 REST 架构风格