1 Нижеследующее больше указывает разработчикам кода на проблему рельсов, которая может быть воспринята как недостаток.
2 А также я спрашиваю мнение людей, которые лучше разбираются в этом.
Я хочу добавить WebDAV в свое приложение Rails 3 с аутентификацией Warden. Промежуточное ПО моего надзирателя внедряется через Devise.
http://github.com/chrisroberts/dav4rack
http://github.com/hassox/warden
http://github.com/plataformatec/devise
Я не могу монтировать обработчики DAV4Rack из приложения rails (маршруты), например:
# in config/routes.rb
mount DAV4Rack::Handler.new(
:root => Rails.root.to_s, # <= it's just an example
:root_uri_path => '/webdav',
:resource_class => Dav::DocumentResource # <= my custom resource, you could use FileResource from dav4rack
), :at => "/webdav"
потому что rails проверяет глаголы HTTP (GET POST PUT ..), а webdav использует расширения HTTP, такие как PROPFIND, которые не проверяются, выдавая следующее исключение:
ActionController::UnknownHttpMethod (PROPFIND, accepted HTTP methods are get, head, put, post, delete, and options)
Эта проверка выполняется в ActionDispatch:
/usr/local/lib/ruby/gems/1.9.1/gems/actionpack-3.0.0/lib/action_dispatch/http/request.rb +56 +72
in (56) "def request_method" and (72) "def method"
Пример кода из ActionDispatch, который выполняет проверку, чтобы было понятно:
def method
@method ||= begin
method = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD']
HTTP_METHOD_LOOKUP[method] || raise(ActionController::UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
method
end
end
Теоретически мы могли бы исправить эту проверку, чтобы она соответствовала глаголам webdav, таким как проект railsdav раньше делал (обратите внимание, что там рельсы 2, в рельсах 3 нужно пропатчить action_dispatch/http/request).
Чтобы добавить обработчики DAV4Rack в приложение rails, мне нужно смонтировать обработчик вне ActionDispatch на уровне стойки, например так:
# config.ru
require ::File.expand_path('../config/environment', __FILE__)
require 'dav4rack/interceptor'
require 'dav/document_resource'
app = Rack::Builder.new{
map '/webdav/' do
run DAV4Rack::Handler.new(
:root => Rails.root.to_s,
:root_uri_path => '/webdav',
:resource_class => Dav::DocumentResource
)
end
map '/' do
use DAV4Rack::Interceptor, :mappings => {
'/webdav/' => {
:resource_class => Dav::DocumentResource
},
}
run Pmp::Application
end
}.to_app
run app
Теперь у меня есть поддержка Webdav в моем приложении. Но для этого все еще требуется аутентификация, и для этого я хотел бы использовать надзирателя.
# in document_resource.rb
def check_authentication
puts request.env['warden'] # nil :(
end
Warden равен нулю, потому что мой DAV4Rack::Handler монтируется над промежуточным программным обеспечением сеанса и надзирателя. Используя «промежуточное ПО rake» для проверки моего стека, я вижу следующее:
> rake middleware
use ActionDispatch::Static
use Rack::Lock
use ActiveSupport::Cache::Strategy::LocalCache
use Rack::Runtime
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::RemoteIp
use Rack::Sendfile
use ActionDispatch::Callbacks
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Rack::MethodOverride
use ActionDispatch::Head
use ActionDispatch::BestStandardsSupport
use Warden::Manager
run Pmp::Application.routes
Я полагаю, что обернув "Pmp::Application.routes" обработчиком DAV (точно так же, как я сделал выше для "Pmp::Application" в config.ru), мой обработчик webdav будет внедрен в стек в нужном месте, чтобы удовлетворить два условия:
- Будьте выше кода проверки метода ActionDispatch, чтобы избежать ActionController::UnknownHttpMethod
- Будьте ниже сеанса и Warden::Manager, чтобы я мог использовать аутентификацию надзирателя.
Как это сделать? Глядя на вывод «rake middleware», кажется очевидным переопределение метода «Pmp::Application.routes»:
# in my app at APP_ROOT/config/application.rb
# override the routes method inherited from Rails::Application#routes
def routes
routes_app = super
app = Rack::Builder.new {
map '/webdav/' do
run DAV4Rack::Handler.new(
:root => Rails.root.to_s,
:root_uri_path => '/webdav',
:resource_class => Dav::DocumentResource
)
end
map '/' do
use DAV4Rack::Interceptor, :mappings => {
'/webdav/' => {
:resource_class => Dav::DocumentResource
},
}
run routes_app
end
}.to_app
class << app; self end.class_eval do
attr_accessor :routes_app
def method_missing(sym, *args, &block)
routes_app.send sym, *args, &block
end
end
app.routes_app = routes_app
app
end
Поскольку нашему новому стоечному приложению «приложение» будет задано несколько методов в цепочке, на которые раньше отвечало старое стоечное приложение «routes_app», мы делегируем их старому исходному приложению «routes_app» с помощью небольшой магии method_missing.
И вуаля: все работает! Огромный успех.
Только одна проблема: мне это не нравится. Должен быть лучший способ сделать все это окружение, кроме переопределения метода маршрутов.
Обратите внимание, что это не работает с пассажиром. Кажется, лучший способ — это патчить рельсы обезьяны.
См.: вики dav4rack
БОЛЬШОЙ ВОПРОС:
ЕСТЬ ЛИ ЛУЧШИЙ СПОСОБ ДОБАВИТЬ СТОЙОЧНОЕ ПРИЛОЖЕНИЕ СРАЗУ НАД ПРИЛОЖЕНИЕМ "Pmp::Application#routes" С ПОМОЩЬЮ МОНТАЖА В СТОЙКУ ИЛИ ДРУГОГО ???
БОЛЬШОЕ ЗАКЛЮЧЕНИЕ
- Семантика "монтирования" в route.rb должна быть на уровне стойки (не rails/railtie/что-то еще), чтобы таким образом разрешить обработку расширений HTTP или, по крайней мере, иметь метод для этого случая "mount_rack"
match '/webdav' => DAV4Rack::Handler.new
в ваших маршрутах и config.middleware.insert(DAV4Rack::Interceptor) в вашемapplication.rb
? - person balu   schedule 10.06.2011