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

前端 未结 2 1522
小蘑菇
小蘑菇 2020-12-25 14:11

I\'d like to have MyMiddleware run in my Rack app, but only for certain paths. I was hoping to use Rack::Builder or at least Rack::URLMap

相关标签:
2条回答
  • 2020-12-25 15:03

    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
        #...
      end
    end
    

    Or, you could use URLMap w/o the dslness. It would look something like this:

    main_app = MainApp.new
    Rack::URLMap.new '/'=>main_app, /^(foo|bar)/ => MyMiddleWare.new(main_app)
    

    URLMap is actually pretty simple to grok.

    0 讨论(0)
  • 2020-12-25 15:11

    This doesn't work because @app doesn't exist in the right scope:

    # in my_app.ru or any Rack::Builder context:
    @app = self
    map '/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 = self
    map '/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 = self
    use Rack::ShowExceptions
    use Rack::Lint
    use Rack::Reloader, 0
    use Rack::ContentLength
    
    map '/html' do
      use MyContentTypeSettingMiddleware, 'text/html'
      run lambda { |env| puts 'HTML!'; ::MAIN_APP.call(env) }
    end
    
    map '/xml' do
      use MyContentTypeSettingMiddleware, 'application/xml'
      run lambda { |env| puts 'XML!'; ::MAIN_APP.call(env) }
    end
    
    map '/' 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'.

    0 讨论(0)
提交回复
热议问题