如何使我的 Web 应用程序无状态但仍能做一些有用的事情?
Posted
技术标签:
【中文标题】如何使我的 Web 应用程序无状态但仍能做一些有用的事情?【英文标题】:How do I make my Web Application stateless yet still do something useful? 【发布时间】:2011-03-02 06:34:30 【问题描述】:我看过这个建议...
理想情况下,Web 应该遵循 REST 原则并且完全无状态。因此,单个 URL 应该标识单个资源,而不必保留每个用户的导航历史记录。
...我阅读了***页面http://en.wikipedia.org/wiki/REST,听起来确实不错,但我不知道如何实际实现它。我正在使用 ASP .NET Webforms 而不是 MVC。
例如,在我将要构建的应用程序中 - 我需要我的用户登录才能允许他们执行任何操作。在允许他们做很多有用的事情之前,他们必须跳过一些障碍——比如接受 T 和 C,并确认他们的基本细节没有改变。最后他们被允许做他们真正想做的事情,比如 BuyAProduct!
在我看来(我来自富客户端的大量有状态世界)我需要状态来记录他们所做的事情并从中推断出他们被允许做什么。我看不出我如何支持他们(比如)为 BuyAProduct URI 添加书签。当他们到达书签时,我如何知道他们是否已登录,他们是否同意 T 和 C,以及他们是否尽职尽责地检查了他们的基本详细信息?
我喜欢应用程序无状态的想法,部分原因是它似乎完全解决了“当用户单击后退和前进按钮时我该怎么办?”的问题。我不知道如何才能让它正常工作。我觉得我错过了一些真正重要的东西。
【问题讨论】:
为什么?如果它目前正在工作,为什么要改变? Web 应用程序往往需要有状态,因此需要有 Sessions 和 Cookies。 啊-它还没有工作,因为它还没有构建。我正在考虑设计选项。我将编辑问题。 @John 我可以保证 Web 应用程序不需要使用会话和 cookie。 @Darrel Miller:如果你想让用户登录应用怎么办? @John http Authorization 标头允许您在请求中提供凭据,以确保发出请求的人是他们所说的人,并且有足够的授权来发出请求。什么是“登录”无法通过 Authorization 标头实现? 【参考方案1】:该建议并不是建议 app 应该是无状态的 - 它是建议应用程序中的资源应该是无状态的。也就是说,名为“www.mysite.com/resources/123”的页面将始终代表相同的资源,无论哪个用户正在访问它或他们是否已登录。
(您可能拒绝未登录的用户访问是一个单独的问题 - 关键是 Uri 本身不依赖于用户特定的数据来工作。)
例如,违反此规则的网站类型是那些您导航到产品页面,将 Uri 通过电子邮件发送给您的朋友,然后单击它时,他们会看到一条类似“对不起,您的会话已过期”或“此产品不存在”或类似内容。发生这种情况的原因是因为 Uri 包含特定于站点上用户会话的内容,并且如果其他用户尝试使用该链接(或稍后的同一用户),它不再有效。
因此,您的应用程序仍然需要某种形式的状态,但实现该状态的位置是重要因素。
希望能有所帮助!
【讨论】:
请注意,在某些情况下,资源是特定于用户的,因此只有授权用户才能看到某些资源而不是其他资源。因此,可以使用特定于用户的交互。 是的 - 但该资源的 Uri 对于所有用户来说都应该保持不变,无论他们是否有权访问。 (例如,如果这是一个 REST 应用程序,您将让 Uri 返回相关的 Http Not Authorized 状态,而不是资源)。 所以必须在客户端维护状态?比如说我有一个人www.XXXX.com/persom/Tom的链接,这个网站会拒绝陌生人访问,如果我是他的朋友,我复制这个链接,我会看到他的帖子,而如果我我是陌生人,这将被禁止。我可以将这种关系视为一种状态,因为确实相同的 URI 给了我们不同的页面。它必须有一些东西需要在服务器中维护,例如在数据库中维护关系。如果这是一种状态,那么这个应用程序是无状态的吗? 不过,您的同一个 URL 不应提供不同的页面。从技术上讲,应该发生的是,未经授权的 URL 应该给出HTTP 401
响应,并且该 URL 的错误页面可以被定制以显示有用的东西(如登录表单)。尽管最终结果可能会在用户看到不同的页面时出现,但您的 URL 将仅作为 成功 请求的结果提供单个资源。这是一个细微的差别,但却是无状态模型的精髓。【参考方案2】:
如果您想做 Web 表单,那很酷。如果你想做 REST,那也很酷。但是,出于对一切神圣事物的热爱,请不要尝试使用 Web 表单来遵守 REST 的原则。
为了进一步澄清这一点,我不认为 WebForms 是 REST 的明智选择,因为 WebForms 所基于的概念模型是您将 Web 抽象出来的模型。它是为模拟 VB 开发模型而构建的。
REST 包含 HTTP 和 Web 应用程序的分布式特性。这两种方法不兼容。
【讨论】:
不,我认为你错了,因为他所说的基本原则同样可以(并且*应该)适用于 WebForms:不要将状态信息放在你的 Uri 中。跨度> @Dan 我认为您的意思是不要在 url 中引用服务器会话状态。将客户端应用程序状态放在 URL 中是一种完全安静的方法。不管会话状态是在 URL 中还是以其他方式(如 cookie)进行通信,会话状态都会导致违反 REST 自描述约束。 Uri 中的应用程序状态完全违背 REST 的原则:如果 your Uri to a resource 不同于 my Uri to a resource ,这不是 RESTful Uri。 @Dan 将查询参数添加到 URL 后,您将引用不同的资源。识别资源的不仅仅是路径段。 @Darrel 我认为 Dan 暗指的是 Richardson 成熟度模型:code.google.com/p/implementing-rest/wiki/RMM。是的,你可以这样做,是的,你可以称之为 RESTful。但它真的是 RESTful 吗?【参考方案3】:可以保持资源状态。 “无状态禁止”仅指会话状态。
这是Roy Fielding's seminal REST derivation的摘录:
接下来我们为客户端-服务器交互添加一个约束: 通信必须是无国籍的,如在 第 3.4.3 节的 client-stateless-server (CSS) 样式(图 5-3), 这样从客户端到服务器的每个请求都必须包含所有 理解请求所必需的信息,并且不能采取 服务器上存储的任何上下文的优势。会话状态是 因此完全保留在客户端。
【讨论】:
当客户端(Web 浏览器)无法存储任何安全信息(又名凭据)时,这怎么可能?【参考方案4】:事情是这样的:REST 是关于通过无状态协议进行的有状态通信。这并不是说 REST 是无状态的。 WebForms 使您能够保留请求之间的状态。为什么这是必要的?它让您可以使用向上/向下按钮对列表中的项目进行排序,而无需在每次单击时更新底层资源。你不需要那个。您可以只 PUT 资源表示以使其看起来正确,或者使用 javascript 编辑您的表示,然后在您满意后在最后执行 PUT。 (请注意,我使用的是 PUT,而不是 POST。您真正要做的是替换表示,以便将来的 GET 检索正确的状态。)
WebForms 对所有事情都使用 POST。当您创建一个新项目并且不知道它会在哪里时,您应该只发布到一个 URL。如果您知道它的 url,那么您应该使用 PUT 来创建或替换。如果您需要购物车的中间步骤,那么您应该为这些中间步骤创建资源表示。您的浏览器和服务器通过在彼此之间传递完整表示进行通信。这是简单的请求/响应消息传递。
WebForms 不鼓励这样做。您可以在 WebForms 中构建 RESTful 系统,但默认模型会将您从它推向 RPC 方法。我可以想到两种解决方法:Front Controller(在这种情况下,您应该真正考虑 MVC)或对几乎所有内容使用 .ashx 文件。 Postback 模型很好地消除了使用真正的 WebForms/.aspx 进行真正 REST 的任何真正希望(即 PUT 和 DELETE 始终是 POST,因此使 REST 模型失败)。
【讨论】:
根据 RFC2616 POST 可用于将实体发送到处理资源。这并不意味着创建资源。创建只是 POST 的一种用途。不需要使用 PUT 和 DELETE 来实现 RESTful。请参阅此常见问题解答code.google.com/p/implementing-rest/wiki/FAQ @Darrel 够公平的。我可能过于倾向于明确。我看到的问题是它太容易解释一切,这又回到了 RPC。 @Darrel 我关注的是 Richardson 成熟度模型的第 3 级:code.google.com/p/implementing-rest/wiki/RMM。好吧,也许是第 2 级。:) 是的,如果人们对其他 REST 约束没有清楚的了解,那么在很多事情上使用 POST 很容易导致回到 RPC。【参考方案5】:cookie 似乎是您问题的答案。您可以使用将设置 cookie 的 .net 身份验证提供程序,您的应用程序可以检查该 cookie 并要求他们在购买任何东西时存在。
您要尝试避免的事情是在服务器上保留它们的状态表示,也就是会话 cookie。这将使扩展更加困难。
【讨论】:
以上是关于如何使我的 Web 应用程序无状态但仍能做一些有用的事情?的主要内容,如果未能解决你的问题,请参考以下文章