How do I use a Rack middleware only for certain paths?
You could have MyMiddleware check the path and not pass control to the next piece of middle ware if it matches.
class MyMiddleware def initialize app @app = app end def call env middlewary_stuff if env['PATH_INFO'] == '/foo' @app.call env end def middlewary_stuff #... endend
Or, you could use URLMap w/o the dslness. It would look something like this:
main_app = MainApp.newRack::URLMap.new '/'=>main_app, /^(foo|bar)/ => MyMiddleWare.new(main_app)
URLMap is actually pretty simple to grok.
This doesn't work because @app
doesn't exist in the right scope:
# in my_app.ru or any Rack::Builder context:@app = selfmap '/foo' do use MyMiddleware run lambda { |env| @app.call(env) }end
But this will:
# in my_app.ru or any Rack::Builder context:::MAIN_RACK_APP = selfmap '/foo' do use MyMiddleware run lambda { |env| ::MAIN_RACK_APP.call(env) }end
Rack::Builder
strips the first argument to map
off the front of the path, so it doesn't endlessly recurse. Unfortunately, this means that after that path prefix is stripped off, it's unlikely that the rest of the path will properly match other mappings.
Here's an example:
::MAIN_APP = selfuse Rack::ShowExceptionsuse Rack::Lintuse Rack::Reloader, 0use Rack::ContentLengthmap '/html' do use MyContentTypeSettingMiddleware, 'text/html' run lambda { |env| puts 'HTML!'; ::MAIN_APP.call(env) }endmap '/xml' do use MyContentTypeSettingMiddleware, 'application/xml' run lambda { |env| puts 'XML!'; ::MAIN_APP.call(env) }endmap '/' do use ContentType, 'text/plain' run lambda { |env| [ 200, {}, "<p>Hello!</p>" ] }end
Going to /html/xml
causes the following to go to the log:
HTML!XML!127.0.0.1 - - [28/May/2009 17:41:42] "GET /html/xml HTTP/1.1" 200 13 0.3626
That is, the app mapped to '/html'
strips of the '/html'
prefix and the call now matches the app mapped to '/xml'
.