How do I use a Rack middleware only for certain paths? How do I use a Rack middleware only for certain paths? ruby ruby

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'.