Reputation: 109333
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
Upvotes: 39
Views: 29940
Reputation: 48318
Short answer: Use request.env
or env
inside a controller.
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
.
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.
In the controller...
request.env
or env
to get at the raw environment objectparams
to read Rack query strings and post data from the rack input stream. (E.g. Rack::Request#params
)session
to access the value of rack.session
in the rack environmentIn the middleware...
rack.session
property on the environment hashRack::Request#params
Rack::Request#update_param
and Rack::Request#delete_param
(as stated in the docs for Rack::Request#params
) ActionDispatch::Http::Parameters#params
through ActionDispatch::Request
Upvotes: 28
Reputation: 65232
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
Upvotes: 31