问题描述
短版
在 OmniAuth 中使用动态路由段时,Devise 的官方建议是使用多个 devise_for
调用:(Source)
devise_for :users,only: :omniauth_callbacks,controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
scope '/(:locale)',locale: /ru|en/ do
devise_for :users,skip: :omniauth_callbacks
end
但对我来说,这根本行不通,因为第一个 devise_for
调用的设置丢失了(即覆盖的控制器消失了),只保留了第二个调用的配置。
我在这里遗漏了什么?
在我看来,它永远无法工作,因为每个 devise_for
调用都会替换之前的映射及其所有设置。
另一方面,它是官方记录的方式,并且在设计问题跟踪器中对该解决方案有一些赞扬。
更多详情
设计源代码
def devise_for(*resources)
# ...
resources.each do |resource|
mapping = Devise.add_mapping(resource,options)
# ...
end
#...
end
def self.add_mapping(resource,options)
mapping = Devise::Mapping.new(resource,options)
@@mappings[mapping.name] = mapping
# ...
end
如您所见,任何以前的映射及其配置都已替换为新的映射。 根据 git blame 的说法,这段代码在过去 11 年里没有改变。
调试
我对 Devise Wiki 中给出的代码使用了调试器:(Source)
devise_for :users,controllers: { omniauth_callbacks: 'auth/oauth_callbacks' }
scope '(:locale)' do
devise_for :users,skip: :omniauth_callbacks,controllers: { passwords: 'auth/passwords',registrations: 'auth/registrations' }
end
在第一个 devise_for
调用之后,设计映射中的控制器配置如下所示:
Devise.mappings.first[1].controllers = Hash (1 element)
:omniauth_callbacks => "auth/oauth_callbacks"
第二次调用后:
Devise.mappings.first[1].controllers = Hash (5 elements)
:passwords => "auth/passwords"
:registrations => "auth/registrations"
:sessions => "devise/sessions"
:confirmations => "devise/confirmations"
:unlocks => "devise/unlocks"
如您所见,原始控制器配置已消失。
设计维基,问题
以下 Wiki 文章、问题和评论表明,使用多个 devise_for
调用实际上是有效的:
- https://github.com/heartcombo/devise/wiki/How-To:-OmniAuth-inside-localized-scope
- https://github.com/heartcombo/devise/issues/3651#issue-90422948
- https://github.com/heartcombo/devise/pull/2227#issuecomment-31399288
我在这里严重怀疑自己:一方面,这是将 OmniAuth 与动态段一起使用时的官方方法,并伴有其他用户的各种确认。另一方面,很明显(恕我直言)这是行不通的。
我在这里遗漏了什么吗?
编辑:
经过进一步调查,我越来越确定这在 Devise 中从未真正起作用。没有人注意到的原因是,在访问缺少的控制器配置时,Devise 会自动生成默认配置(在这种情况下为 re-generates)
在Devise::Mapping#default_controllers
中:(Source)
def default_controllers(options)
mod = options[:module] || "devise"
@controllers = Hash.new { |h,k| h[k] = "#{mod}/#{k}" } # <----------- DEFAULT
@controllers.merge!(options[:controllers]) if options[:controllers]
@controllers.each { |k,v| @controllers[k] = v.to_s }
end
因此,如果有人只想从动态路线段中提取路线 omniauth 模块,记录的方式实际上确实有效,但这或多或少是偶然的。第一次调用 devise_for
仍然不能省略,因为它生成路由和辅助方法(由 Rails 维护并通过多次调用维护)。但是 controllers: ...
选项也可以省略,自动生成的配置将启动。
因此,如果您想要 Devise 的集成 omniauth 控制器,官方解决方案还可以,如果您确实需要不同的控制器,则不需要。
编辑 2:
在第二个 devise_for
调用后添加此行可恢复控制器配置。有了它,推荐的解决方案实际上有效:
Devise.mappings[:user].controllers[:omniauth_callbacks] = 'users/omniauth_callbacks'
我向 Devise 问题跟踪器添加了一个问题,这似乎确实是一个错误,或者至少是一个意外的边缘情况。 (Issue)
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)