2009-05-28 12 views
16

Je voudrais avoir exécuté dans mon application Rack, mais uniquement pour certains chemins. J'espérais utiliser Rack::Builder ou au moins Rack::URLMap, mais je n'arrive pas à comprendre comment.Comment utiliser un middleware Rack uniquement pour certains chemins?

C'est ce que je pensais travailler, mais qui ne fonctionne pas:

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

Ou, mieux encore, avec une expression rationnelle:

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

Mais map semble exiger une application à la fin ; il ne retombera pas sur le simple renvoi du contrôle à son parent. (L'erreur réelle est « undefined method 'each' for nil:NilClass » de quand rack essaie de tourner la fin de ce bloc do...end dans un app.)

Y at-il un middleware là-bas qui prend un tableau de intergiciels et un chemin et ne les exécute si la chemin correspond?

+1

stupide faute de frappe - merci d'avoir corrigé cela, AnthonyWJones! :: bloque la tête dans la honte :: –

Répondre

13

Vous pouvez demander à MyMiddleware de vérifier le chemin et de ne pas passer le contrôle à la partie intermédiaire suivante si elle correspond.

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 

Ou, vous pouvez utiliser URLMap sans la dslness. Il ressemblerait à quelque chose comme ceci:

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

URLMap est en fait pretty simple to grok.

+0

[Rack :: URLMap] (http://rack.rubyforge.org/doc/Rack/URLMap.html) ne supporte malheureusement pas l'utilisation d'expressions régulières comme partie de la localisation (clé de hachage) , donc la deuxième partie de cette réponse n'est pas correcte. –

10

Cela ne fonctionne pas parce que @app n'existe pas dans le champ d'application droite:

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

mais cela:

# 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 des bandes le premier argument de map hors le devant de la chemin, donc il ne se recourt pas indéfiniment. Malheureusement, cela signifie qu'après le retrait de ce préfixe de chemin, il est peu probable que le reste du chemin corresponde correctement aux autres mappages.

Voici un exemple:

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

Aller à /html/xml cause ce qui suit pour aller dans le journal:

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

C'est, l'application mappée à '/html' bandes du préfixe '/html' et l'appel correspond maintenant à l'application mappée à '/xml'.