How do I access the Rack environment from within Rails?
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' ... endend
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' ... endend
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
andRack::Request#delete_param
(as stated in the docs forRack::Request#params
) - Update params in a Rails specific way using
ActionDispatch::Http::Parameters#params
throughActionDispatch::Request