cly*_*yfe 7 ruby rack webdav ruby-on-rails http
1以下更多内容是指代代码开发一个可以被认为是缺陷的rails的问题.
2我还要问一些了解得更好的人.
我想通过Warden身份验证将WebDAV添加到我的Rails 3应用程序中.我的warden中间件是通过Devise注入的.
http://github.com/chrisroberts/dav4rack
http://github.com/hassox/warden
http://github.com/plataformatec/devise
我无法从rails app(路由)中挂载DAV4Rack处理程序,如下所示:
# 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"
Run Code Online (Sandbox Code Playgroud)
因为rails验证HTTP谓词(GET POST PUT ..),并且webdav使用像PROPFIND这样的HTTP扩展,但是没有验证,抛出以下异常:
ActionController::UnknownHttpMethod (PROPFIND, accepted HTTP methods are get, head, put, post, delete, and options)
Run Code Online (Sandbox Code Playgroud)
此验证在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"
Run Code Online (Sandbox Code Playgroud)
来自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
Run Code Online (Sandbox Code Playgroud)
从理论上讲,我们可以对此验证进行修补以符合webdav谓词,例如用于执行的railsdav项目(注意那里是rails 2,在rails 3中需要使用monkey-patch 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
Run Code Online (Sandbox Code Playgroud)
现在我在我的应用程序中有Webdav支持.但它仍然需要身份验证,为此我想使用warden.
# in document_resource.rb
def check_authentication
puts request.env['warden'] # nil :(
end
Run Code Online (Sandbox Code Playgroud)
Warden是零,因为我的DAV4Rack :: Handler安装在会话之上并且监控中间件.使用"rake middleware"来检查我的堆栈,我可以看到以下内容:
> 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
Run Code Online (Sandbox Code Playgroud)
我相信通过使用DAV处理程序包装"Pmp :: Application.routes"(就像我在上面为config.ru中的"Pmp :: Application"所做的那样)会将我的webdav处理程序注入到正确位置的堆栈中以满足两者条件:
怎么做?看看"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
Run Code Online (Sandbox Code Playgroud)
因为我们的新机架应用程序"应用程序"将被问及链中的一些方法,即旧的机架应用程序"routes_app"用于重新加载,我们将theese委托给旧的原始应用程序"routes_app",带有一点method_missing魔法.
瞧,一切正常!巨大的成功.
只有一个问题:我不喜欢它.除了覆盖路线方法之外,必须有更好的方法来完成所有这些包络.
请注意,这不适用于乘客.最好的方法似乎是猴子修补铁轨.
请参阅:dav4rack wiki
是否有更好的方法添加一个机架应用程序上面的"Pmp ::应用程序#路由"APP通过机架或其他方式???