2009-05-28 18 views
16

Mi piacerebbe avere MyMiddleware eseguito nella mia app Rack, ma solo per determinati percorsi. Speravo di usare Rack::Builder o almeno Rack::URLMap, ma non riesco a capire come.Come si utilizza un middleware Rack solo per determinati percorsi?

Questo è quello che pensavo avrebbe funzionato, ma non lo fa:

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

O, meglio ancora, con un Regexp:

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

Ma map sembra richiedere un app alla fine ; non ricadrà sul controllo passatempo sul genitore. (L'errore effettivo è "undefined method 'each' for nil:NilClass" da quando Rack cerca di trasformare la fine di tale do...end blocco in un app.)

C'è un middleware là che accetta un array di middleware e un percorso e li esegue solo se il partite di percorso?

+1

errore stupido - grazie per averlo corretto, AnthonyWJones! :: pende la testa nella vergogna :: –

risposta

13

Si potrebbe avere MyMiddleware controllare il percorso e non passare il controllo al prossimo pezzo di middle ware se corrisponde.

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 

Oppure, si potrebbe utilizzare URLMap w/o il dslness. Sembrerebbe qualcosa di simile:

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

URLMap è in realtà pretty simple to grok.

+0

[Rack :: URLMap] (http://rack.rubyforge.org/doc/Rack/URLMap.html) sfortunatamente non supporta l'uso di espressioni regolari come la posizione (chiave hash) parte , quindi la seconda parte di questa risposta non è corretta. –

10

Ciò non funziona perché @app non esiste nell'area destra:

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

Ma questo sarà:

# 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 strisce primo argomento map fuori la parte anteriore del percorso, quindi non ricorre all'infinito. Sfortunatamente, questo significa che dopo che il prefisso del percorso è stato rimosso, è improbabile che il resto del percorso corrisponda correttamente ad altri mapping.

Ecco un esempio:

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

Andare /html/xml provoca il seguente per andare al registro:

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

Cioè, l'applicazione mappato '/html' strisce di '/html' prefisso e la chiamata ora corrisponde all'app mappata a '/xml'.

Problemi correlati