为什么我对 OmniAuth 的多个 devise_for 调用不起作用?

问题描述

短版

在 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 调用都会替换之前的映射及其所有设置。

另一方面,它官方记录的方式,并且在设计问题跟踪器中对该解决方案有一些赞扬。

更多详情

设计源代码

代码如下:(Source)

def devise_for(*resources)
  # ...
  resources.each do |resource|
    mapping = Devise.add_mapping(resource,options)
    # ...
  end
  #...
end

还有 add_mapping代码:(Source)

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 调用实际上是有效的:

在这里严重怀疑自己:一方面,这是将 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 (将#修改为@)