ruby - rails - ¿Cómo uso un middleware de Rack solo para ciertas rutas?
ruby rack web server (2)
Me gustaría que MyMiddleware
ejecute en mi aplicación Rack, pero solo para ciertas rutas. Esperaba usar Rack::Builder
o, al menos, Rack::URLMap
, pero no sé cómo.
Esto es lo que pensé que funcionaría, pero no lo hace:
# in my rackup file or Rails environment.rb:
map ''/foo'' do
use MyMiddleware, { :some => ''options'' }
end
O, mejor aún, con un Regexp:
map /^foo/ do
use MyMiddleware, { :some => ''options'' }
end
Pero el map
parece exigir una aplicación al final; no retrocederá simplemente pasando el control a su padre. (El error real es el " undefined method ''each'' for nil:NilClass
" cuando Rack intenta convertir el final de ese bloque do...end
en una app
).
¿Hay un middleware por ahí que toma una serie de middlewares y una ruta y solo los ejecuta si la ruta coincide?
Esto no funciona porque @app
no existe en el ámbito correcto:
# 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 va a:
# 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
elimina el primer argumento para map
el frente de la ruta, por lo que no repite sin cesar. Desafortunadamente, esto significa que después de eliminar el prefijo de la ruta, es poco probable que el resto de la ruta coincida con otras asignaciones.
Aquí hay 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 lo siguiente vaya 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 asignada a ''/html''
tiras ''/html''
prefijo ''/html''
y la llamada ahora coincide con la aplicación asignada a ''/xml''
.
Podría 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, puede 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 bastante simple de asimilar .