如何从 Rails 中访问 Rack 环境?
Posted
技术标签:
【中文标题】如何从 Rails 中访问 Rack 环境?【英文标题】:How do I access the Rack environment from within Rails? 【发布时间】:2010-10-25 07:37:02 【问题描述】:我有一个如下所示的 Rack 应用程序:
class Foo
def initialize(app)
@app = app
end
def call(env)
env["hello"] = "world"
@app.call(env)
end
end
将我的 Rack 应用程序连接到 Rails 后,如何从 Rails 中访问env["hello"]
?
更新:感谢 Gaius 的回答。 Rack and Rails 允许您在请求期间或会话期间存储内容:
# in middleware
def call(env)
Rack::Request.new(env)["foo"] = "bar" # sticks around for one request
env["rack.session"] ||=
env["rack.session"]["hello"] = "world" # sticks around for duration of session
end
# in Rails
def index
if params["foo"] == "bar"
...
end
if session["hello"] == "world"
...
end
end
【问题讨论】:
您确定Rack::Request.new(env)["foo"] = "bar"
改变了Rails 的任何内容吗?从源代码中我可以看到,应该改变的只是Rack::Request
对象本身的一个实例变量。甚至the docs for that method 说:“请注意,修改不会保留在环境中。如果您想破坏性地修改参数,请使用 update_param 或 delete_param。”
【参考方案1】:
我很确定您可以使用 Rack::Request
对象来传递请求范围变量:
# middleware:
def call(env)
request = Rack::Request.new(env) # no matter how many times you do 'new' you always get the same object
request[:foo] = 'bar'
@app.call(env)
end
# Controller:
def index
if params[:foo] == 'bar'
...
end
end
或者,您可以直接获取“env
”对象:
# middleware:
def call(env)
env['foo'] = 'bar'
@app.call(env)
end
# controller:
def index
if request.env['foo'] == 'bar'
...
end
end
【讨论】:
(我知道它已经过时了)我相信第一个陈述不再正确(Rails 4+)。Rack::Request.new(env)
每次都返回新对象,因此您不能在其中保留任何内容。 sources【参考方案2】:
简答:在控制器中使用request.env
或env
。
长答案:
根据Rails Guide on Rails controllers,ActionController 提供了一个request
方法,您可以使用该方法访问有关您的控制器正在响应的当前HTTP 请求的信息。
在进一步检查 ActionController::Base#request
的文档后,我们看到它“返回一个代表当前请求的 ActionDispatch::Request 实例。”
如果我们查看ActionDispatch::Request
的文档,我们会发现它继承自Rack::Request
。啊哈!我们开始吧。
现在,如果您不熟悉 Rack::Request
的文档,它基本上是 Rack 环境的封装。因此,在大多数情况下,您应该能够按原样使用它。如果您确实想要原始环境哈希,您可以使用Rack::Request#env
获得它。所以在 Rails 控制器中,就是request.env
。
深入挖掘:
在进一步检查ActionController::Base
的实例方法后,我注意到那里没有太多可看的东西。特别是,我注意到 params
和 session
变量似乎丢失了。所以,我上一级到ActionController::Metal
,ActionController::Base
继承自。
在ActionController::Metal
中,我发现了一个方法env
,它没有关于它的作用的文档——但我可以猜到。事实证明我是对的。那个变量是being assigned to request.env
。
ActionController::Metal
还包含params
方法,根据the source,默认设置为request.parameters
。事实证明,request.parameters
不是来自Rack::Request
,而是来自ActionDispatch::Http::Parameters,它包含在ActionDispatch::Request
中。此方法与Rack::Request#params
方法非常相似,不同之处在于更改它会修改特定于 Rails 的 Rack 环境变量(因此更改将在 ActionDispatch::Request
的实例中保持不变)。
但是,我似乎仍然找不到 session
方法。事实证明,它根本不在文档中。在搜索了ActionController::Metal
的源码后,终于在this line上找到了。没错,就是request.session的快捷方式。
总结一下:
在控制器中...
使用request.env
或env
获取原始环境对象
使用params
读取机架查询字符串并从机架输入流中发布数据。 (例如Rack::Request#params
)
在机架环境中使用session
访问rack.session
的值
在中间件中...
通过环境哈希以通常的方式访问环境属性 通过环境哈希上的rack.session
属性访问 Rails 会话
通过Rack::Request#params
读取参数
通过Rack::Request#update_param
和Rack::Request#delete_param
更新参数(如Rack::Request#params
的文档中所述)
使用 ActionDispatch::Http::Parameters#params
到 ActionDispatch::Request
以特定于 Rails 的方式更新参数
【讨论】:
有助于阅读更深入的部分,尤其是关于参数的部分env
在 rails 5.0 ActionController github.com/rails/rails/commit/… 已弃用以上是关于如何从 Rails 中访问 Rack 环境?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 ruby-on-rails 应用程序上使用 Rack 发送 POST 模拟请求?
如何获取 Rails 应用程序的 CORS 标头以访问 aws s3 存储桶?
Ruby on Rails:无法加载rack / handler /