Как использовать промежуточное ПО Rack только для определенных путей?
Я хочу, чтобы MyMiddleware
запускался в моем приложении Rack, но только для определенных путей. Я надеялся использовать Rack::Builder
или, по крайней мере, Rack::URLMap
, но я не могу понять, как это сделать.
Это то, что я думал, будет работать, но не делает:
# in my rackup file or Rails environment.rb:
map '/foo' do
use MyMiddleware, { :some => 'options' }
end
Или, еще лучше, с Regexp:
map /^foo/ do
use MyMiddleware, { :some => 'options' }
end
Но map
, кажется, требует приложения в конце; он не будет возвращаться к простому контролю до своего родителя. (Фактическая ошибка: "undefined method 'each' for nil:NilClass
", когда Rack пытается превратить конец этого блока do...end
в app
.)
Существует ли промежуточное ПО, которое принимает массив промежуточных элементов и путь и запускает их только в том случае, если путь соответствует?
Ответы
Ответ 1
Вы могли бы MyMiddleware проверить путь и не передавать управление следующему кусочку средней посуды, если он соответствует.
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
Или вы можете использовать URLMap без dslness. Он будет выглядеть примерно так:
main_app = MainApp.new
Rack::URLMap.new '/'=>main_app, /^(foo|bar)/ => MyMiddleWare.new(main_app)
URLMap на самом деле довольно прост в использовании.
Ответ 2
Это не работает, потому что @app
не существует в правой области:
# in my_app.ru or any Rack::Builder context:
@app = self
map '/foo' do
use MyMiddleware
run lambda { |env| @app.call(env) }
end
Но это будет:
# 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
разделяет первый аргумент на map
с передней стороны пути, поэтому он не бесконечно рекурсивно. К сожалению, это означает, что после того, как префикс пути будет удален, маловероятно, что остальная часть пути будет правильно соответствовать другим сопоставлениям.
Вот пример:
::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
Переход к /html/xml
приводит к тому, что в журнал поступает следующее:
HTML!
XML!
127.0.0.1 - - [28/May/2009 17:41:42] "GET /html/xml HTTP/1.1" 200 13 0.3626
То есть, приложение сопоставлено с '/html'
полосами префикса '/html'
, и вызов теперь соответствует приложению, отображаемому на '/xml'
.