Rails 中的 RESTful DCI 上下文
Posted
技术标签:
【中文标题】Rails 中的 RESTful DCI 上下文【英文标题】:RESTful DCI contexts in Rails 【发布时间】:2012-02-25 05:30:12 【问题描述】:我首先通过this blog post 了解了Data, context, and interaction (DCI)。对这个概念着迷,我努力将它构建到我的下一个 Rails 应用程序中。由于 DCI 与 MVC 协同工作,我认为同时使 API 成为 RESTful 不会太难。所以我创建了一个 RESTful 资源 Report
并使用各种上下文对其进行扩展。我在 Rails 中实现上下文的方式是为扩展控制器操作的模块创建一个目录/app/contexts/
。所以我的reports_controller.rb
看起来像这样:
class ReportsController < ApplicationController
before_filter :only => :new do |c|
c.switch_context("submission")
end
# GET /reports
def index
@context.report_list
end
# GET /reports/1
def show
@context.display_report
end
# GET /reports/new
def new
@context.new_report
end
# GET /reports/1/edit
def edit
@context.edit_report
end
# POST /reports
def create
@context.create_report
end
def update
@context.update_report
end
# DELETE /reports/1
def destroy
@context.destroy_report
end
protected
def switch_context(context_name)
session[:context] = context_name
context = session[:context].camelize.constantize
@context ||= self.extend context
end
end
在application_controller.rb
中,我使用before_filter
设置上下文:
class ApplicationController < ActionController::Base
before_filter :contextualize
protect_from_forgery
protected
# Sets the context of both current_user and self
# by extending with /app/roles/role_name
# and /app/contexts/context_name respectively
def contextualize
# Extend self (ActionController::Base) with context
if session[:context]
context_class = session[:context].camelize.constantize
if current_user.allowed_contexts.include?(context_class)
context_class = current_user.context if context_class == Visiting
else
context_class = Visiting
end
else
context_class = current_user.context
end
@context ||= self.extend context_class
end
end
请注意,除了控制器上下文之外,我还使用 Role
扩展了 current_user
。
它是这样工作的:
-
用户登录。
用户的角色是
RegisteredUser
。
RegisteredUser
的默认上下文是 Search
(在 /app/roles/registered_user.rb
中定义)。
在Search
上下文中,用户只能查看已发布的报告。
用户按下“创建新报告”按钮,上下文更改为Submission
并存储在current_user
的会话中。
然后用户继续通过多步骤表单提交报告。
每次用户通过单步执行表单来保存报告时,/app/contexts/submission.rb
上下文都会处理该操作。
还有其他几个上下文(评论、编辑等)和角色(合著者、编辑等)。
到目前为止,这种方法在大多数情况下都运行良好。但是有一个缺陷:当用户打开多个浏览器窗口并更改其中一个窗口的上下文时,所有其他窗口都将处于错误的上下文中。如果用户处于多步骤表单的中间,然后在Search
上下文中打开一个窗口,这可能是一个问题。当他切换回表单并点击“下一步”时,控制器将执行由Search
上下文而不是Submission
上下文定义的操作。
我可以想到两种可能的方法:
-
使用上下文名称命名
Report
资源。因此用户会访问诸如/search/reports
和/submission/reports/1
之类的URL。这对我来说似乎不是 RESTful,我宁愿保持 URL 尽可能干净。
将上下文名称放在隐藏字段中。这种方式需要开发者记住在网站的每一个表单中都放置隐藏字段,并且它不适用于GET请求。
有没有其他方法可以解决这个问题,或者更好的整体实现?
我知道this project,但它对我们的需求来说太有限了。
【问题讨论】:
您是否看过 Rickard Öberg 使用 Qi4J 在 Java 中执行 DCI 和 REST 的示例。我相信他使用 URI 结构来构建上下文,这样请求本身就携带了所有必要的信息来在服务器上重新构建上下文。此外,您可能想尝试将其发布到 object-composition google group。 也许将上一步存储在 cookie 中会有所帮助? 当前上下文已经通过cookie存储在会话中。我看不出存储上一步会增加这种情况。在另一个浏览器窗口中执行某些操作也会使其无用。 对我来说,命名空间 URL 是好的和安静的编译器,因为如果你在搜索上下文或提交上下文中,它不是同一个资源。 资源是一样的,但是你可以用它做什么取决于上下文。但也许你是对的。我只是不太确定 REST 是否知道命名空间是否兼容;更重要的是,它是否会对我们尝试提供的 API 的使用产生不利影响,首先将应用程序设为 RESTful。 【参考方案1】:如果您想允许多个上下文,那么显然您必须将确定当前上下文的信息放在一些不在选项卡之间共享的存储中。在 Rack/Rails 中实现的会话使用 cookie,并且 cookie 在选项卡之间共享。
只需将上下文放入不共享的内容中即可。 context=viewer URL 参数怎么样?
谈到 REST,我认为资源在不同的上下文中是否相同是有争议的。有人可能会争辩说,“访问”用户的报告与“管理”用户的报告不同。在这种情况下,RESTy 方法可能会命名请求(再次将上下文放入 URL),例如/visiting/reports/1 vs /administering/reports/1。
将上下文放入 URL 的第三种方法是将其用作域名的一部分。
【讨论】:
我最终使用了命名空间。以上是关于Rails 中的 RESTful DCI 上下文的主要内容,如果未能解决你的问题,请参考以下文章
javascript 中的数据上下文交互 (DCI) 和事件编程