问题
I have a Rack application that looks like this:
class Foo
def initialize(app)
@app = app
end
def call(env)
env["hello"] = "world"
@app.call(env)
end
end
After hooking my Rack application into Rails, how do I get access to env["hello"]
from within Rails?
Update: Thanks to Gaius for the answer. Rack and Rails let you store things for the duration of the request, or the duration of the session:
# 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
回答1:
I'm pretty sure you can use the Rack::Request
object for passing request-scope variables:
# 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
Alternatively, you can get at that "env
" object directly:
# middleware:
def call(env)
env['foo'] = 'bar'
@app.call(env)
end
# controller:
def index
if request.env['foo'] == 'bar'
...
end
end
回答2:
Short answer: Use request.env
or env
inside a controller.
Long answer:
According to the Rails Guide on Rails controllers, ActionController provides a request
method that you can use to access information about the current HTTP request your controller is responding to.
Upon further inspection of the docs for ActionController::Base#request, we see that it "Returns an ActionDispatch::Request instance that represents the current request."
If we look at the docs for ActionDispatch::Request, we see that it inherits from Rack::Request. Aha! Here we go.
Now, in case you're not familiar with the docs for Rack::Request, it's basically a wrapper around the Rack environment. So for most cases, you should just be able to use it as-is. If you really do want the raw environment hash though, you can get it with Rack::Request#env. So within the Rails controller, that would just be request.env
.
Digging deeper:
After further examining the instance methods of ActionController::Base, I noticed there's not a whole lot there to look at. In particular, I noticed the params
and session
variables seem to be missing. So, I moved up one level to ActionController::Metal, which ActionController::Base inherits from.
In ActionController::Metal, I discovered a method env which had no documentation as to what it did - but I could guess. Turns out I was right. That variable was being assigned to request.env.
ActionController::Metal also contained the params method, which, according to the source, was set to request.parameters by default. As it turns out, request.parameters
isn't from Rack::Request, but ActionDispatch::Http::Parameters, which is included by ActionDispatch::Request. This method is very similar to the Rack::Request#params method, except that altering it modifies a Rails-specific Rack environment variable (and therefore changes will remain persistent across instances of ActionDispatch::Request).
However, I still couldn't seem to find the session
method. Turns out, it's not in the documentation at all. After searching the source code for ActionController::Metal, I finally found it on this line. That's right, it's just a shortcut for request.session.
To summarize:
In the controller...
- Use
request.env
orenv
to get at the raw environment object - Use
params
to read Rack query strings and post data from the rack input stream. (E.g. Rack::Request#params) - Use
session
to access the value ofrack.session
in the rack environment
In the middleware...
- Access properties of the environment the usual way through the environment hash
- Access the Rails session through the
rack.session
property on the environment hash - Read params through Rack::Request#params
- Update params through Rack::Request#update_param and Rack::Request#delete_param (as stated in the docs for Rack::Request#params)
- Update params in a Rails specific way using ActionDispatch::Http::Parameters#params through ActionDispatch::Request
来源:https://stackoverflow.com/questions/848591/how-do-i-access-the-rack-environment-from-within-rails