2009-05-28 25 views
16

Me gustaría que MyMiddleware se ejecute en mi aplicación Rack, pero solo para ciertas rutas. Esperaba usar Rack::Builder o al menos Rack::URLMap, pero no puedo entender cómo.¿Cómo uso un middleware Rack solo para ciertas rutas?

Esto es lo que pensé que trabajar, pero no:

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

O, mejor aún, con una expresión regular:

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

Pero map parece exigir una aplicación en el extremo ; no recurrirá simplemente a pasar el control a su padre. (El error real es "undefined method 'each' for nil:NilClass" de cuando en rack intenta dar vuelta al final de ese do...end bloque en un app.)

¿Existe un middleware por ahí que toma una matriz de middleware y un camino y sólo los ejecuta si el coincide con el camino?

+1

estúpido error tipográfico - ¡gracias por corregir eso, AnthonyWJones! :: cuelga la cabeza en la vergüenza :: –

Respuesta

13

Puede hacer que MyMiddleware compruebe la ruta y no pase el control a la siguiente pieza de middleware si coincide.

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 

O bien, podría utilizar URLMap sin el dslness. Se vería algo como esto:

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

URLMap es en realidad pretty simple to grok.

+0

[Rack :: URLMap] (http://rack.rubyforge.org/doc/Rack/URLMap.html) desafortunadamente no es compatible con el uso de expresiones regulares como la parte de la ubicación (clave hash) , entonces la segunda parte de esta respuesta no es correcta. –

10

Esto no funciona porque @app no existe en el ámbito derecha:

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

pero esto:

# 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 tiras el primer argumento de map de la parte frontal de la camino, por lo que no recurse interminablemente. Desafortunadamente, esto significa que después de que se elimine ese prefijo de ruta, es poco probable que el resto de la ruta coincida adecuadamente con otras asignaciones.

He aquí un ejemplo:

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

ir a /html/xml hace que el siguiente para ir al registro:

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

Es decir, la aplicación asigna a '/html' tiras de la '/html' prefijo y la llamada ahora coincide con la aplicación asignada al '/xml'.

Cuestiones relacionadas