2009-05-28 10 views
16

Ich möchte MyMiddleware in meiner Rack App laufen lassen, aber nur für bestimmte Pfade. Ich hatte gehofft, Rack::Builder oder mindestens Rack::URLMap zu verwenden, aber ich kann nicht recht herausfinden, wie.Wie verwende ich eine Rack-Middleware nur für bestimmte Pfade?

Das ist, was ich dachte funktionieren würde, aber nicht:

# in my rackup file or Rails environment.rb: 
map '/foo' do 
    use MyMiddleware, { :some => 'options' } 
end 

Oder, noch besser, mit einem Regexp:

map /^foo/ do 
    use MyMiddleware, { :some => 'options' } 
end 

Aber map scheint eine App am Ende zu verlangen ; es wird nicht darauf zurückgreifen, die Kontrolle nur an die Eltern zurückzugeben. (Der tatsächliche Fehler „undefined method 'each' for nil:NilClass“ aus, wenn Gestell versucht, das Ende dieses do...end Block in ein app einzuschalten.)

Gibt es eine Middleware aus, dass es eine Reihe von Middleware und einem Weg nimmt und läuft sie nur, wenn das Pfad passt?

+1

dummer Tippfehler - danke für die Korrektur, AnthonyWJones! :: Hängt Kopf in Schande :: –

Antwort

13

Sie könnten MyMiddleware den Pfad überprüfen lassen und die Kontrolle nicht an die nächste Middleware übergeben, wenn sie übereinstimmt.

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 

Oder Sie könnten URLMap ohne dslness verwenden. Es würde wie folgt aussehen:

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

URLMap ist eigentlich pretty simple to grok.

+0

[Rack :: URLMap] (http://rack.rubyforge.org/doc/Rack/URLMap.html) unterstützt leider nicht die Verwendung von regulären Ausdrücken als Speicherort (Hash-Schlüssel) , so ist der zweite Teil dieser Antwort nicht korrekt. –

10

Das funktioniert nicht, weil @app im richtigen Umfang existiert nicht:

# in my_app.ru or any Rack::Builder context: 
@app = self 
map '/foo' do 
    use MyMiddleware 
    run lambda { |env| @app.call(env) } 
end 

aber dies wird:

# 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 streift das erste Argument map aus der Vorderseite des Weg, so dass es nicht endlos rekuriert. Leider bedeutet dies, dass nach dem Entfernen dieses Pfadpräfixes der Rest des Pfads wahrscheinlich nicht mit anderen Zuordnungen übereinstimmt.

Hier ist ein Beispiel:

::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 

zu /html/xml Gehen verursacht die folgende in das Protokoll zu gehen:

HTML! 
XML! 
127.0.0.1 - - [28/May/2009 17:41:42] "GET /html/xml HTTP/1.1" 200 13 0.3626 

, dass die App auf '/html' Streifen des '/html' Präfix und dem Anruf zugeordnet ist, entspricht jetzt der App, die der '/xml' zugeordnet ist.