【问题标题】:How to redirect page after confirmation in Devise在设计中确认后如何重定向页面
【发布时间】:2011-06-27 22:17:03
【问题描述】:

假设用户点击了一个链接到一个受保护的页面。然后,他们将被重定向到可以登录的登录屏幕。如果他们登录了,则成功重定向到该页面。但是,如果他们没有帐户,则必须注册。这就是事情变得棘手的地方,因为我正在做一封电子邮件确认。

通过单击链接会创建一个新会话,我不能自动将用户重定向到该受保护页面。我试图通过在确认链接中添加对重定向的引用来改变这一点。我想做:

<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token, :redirect_to => stored_location_for(@resource)) %>

但我不知道如何访问stored_location_for(或者这是否是正确的访问位置)。它在Devise::Controllers::Helpers 中定义,但它是一个实例方法,所以我不能做Devise::Controllers::Helpers.stored_location_for(…)

如何访问stored_location_for 或者有什么更好的方法?

我的目标是这样做,然后在我的自定义 ConfirmationsController 中定义:

def show
  if params[:redirect_to]
    session["user_return_to"] = params[:redirect_to]
  end
  super

end

应该可以吧?

【问题讨论】:

    标签: ruby-on-rails-3 devise


    【解决方案1】:

    我想通了。我不确定这是否会随着 Devise 昨天在使 Devise::Mailer 将其大部分功能放入模块中所做的更新而改变。 (有关详细信息,请参阅 codeticket)。

    基本上归结为无法访问邮件视图中的session。因此,您必须将重定向作为变量传递。 Devise 在您的资源上使用after_create 方法(在我的情况下为User),然后发送确认电子邮件。这意味着我不能直接将会话变量传递给邮件程序。因此,为了获得此功能,我觉得这是一个非常讨厌的解决方法,但这里是代码:

    要将redirect_to 变量放入邮件程序,您必须向用户添加一个变量,因此:

    class User < ActiveRecord::Base
      …
      attr_accessor :return_to
      …
    end
    

    那么你必须在第一次创建用户时设置那个变量。

    我已经有一个用于注册的自定义控制器设置。 (有关如何设置的信息,请参阅设计的自述文件,或参阅@ramc 的答案以了解方向)。不过这部分做起来还是比较容易的,我只是把它加到参数里面,剩下的就自己搞定了。

    class RegistrationsController < Devise::RegistrationsController
      def create
        params[:user][:return_to] = session[:user_return_to] if session[:user_return_to]
        …
        super
      end
    end
    

    现在用户已经设置了一个变量return_to。我们只需要在confirmation_instructions 电子邮件中访问它。我已经重写了confirmation_instructions.html.erb 的一部分,所以我在里面添加了:

    <% if @resource.return_to %>
      <%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token, :redirect_to => @resource.return_to) %>
    <% else %>
      <%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
    <% end %>
    

    (对于那些不熟悉此的人,@resource 是 Devise 用来定义您的用户的变量)。

    现在,一旦用户点击该链接,我们就需要重定向他们。 @ramc 的 before 过滤器适用于此:

    class ConfirmationsController < Devise::ConfirmationsController
      before_filter :set_redirect_location, :only => :show
    
      def set_redirect_location
        session[:user_return_to] = params[:redirect_to] if params[:redirect_to]
      end
    end
    

    这将处理新用户进入受保护页面然后注册、单击确认链接并正确重定向到受保护页面的情况。

    现在我们只需要处理用户执行上述操作的情况,但他们没有点击链接,而是尝试返回受保护的页面。在这种情况下,他们被要求注册/登录。他们登录,然后被要求确认他们的电子邮件,并可以选择重新发送确认电子邮件。他们输入了他们的电子邮件,现在我们需要将 redirect_to 变量放入新的确认电子邮件中。

    为此,我们需要修改 ConfirmationController,类似于我们修改 RegistrationController 的方式。这次我们需要修改create方法。它开箱即用的工作方式是调用名为send_confirmation_instructions 的用户类方法。我们想重写该方法,以便将return_to 变量传递给它。

    class ConfirmationsController < Devise::ConfirmationsController
      def create
        self.resource = resource_class.send_confirmation_instructions(params[resource_name],session[:user_return_to])
    
        if resource.errors.empty?
          set_flash_message(:notice, :send_instructions) if is_navigational_format?
          respond_with resource, :location => after_resending_confirmation_instructions_path_for(resource_name)
        else
          respond_with_navigational(resource){ render_with_scope :new }
        end
      end
    end
    

    与 Devise 自带的唯一不同的是create 的第一行,我们传入了两个变量。现在我们需要重写那个方法:

    class User < ActiveRecord::Base
      def self.send_confirmation_instructions(attributes={},redirect=nil)
        confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
        confirmable.return_to = redirect if confirmable.persisted?
        confirmable.resend_confirmation_token if confirmable.persisted?
        confirmable
      end
    end
    

    confirmable 成为 User 的一个实例(基于电子邮件的当前用户)。所以我们只需要设置return_to

    就是这样。

    【讨论】:

      【解决方案2】:

      查看stored_location_for在lib/devise/controllers/helpers.rb中的实现方式

      def stored_location_for(resource_or_scope)
         scope = Devise::Mapping.find_scope!(resource_or_scope)
         session.delete("#{scope}_return_to")
      end
      

      可以使用 session['user_return_to'] 以其他方式访问它。在您的情况下,您将丢失该会话对象,因为当用户单击确认邮件中的链接时,它可能会生成一个新会话。

      您可以将建议的任何内容作为前置过滤器实现:

      class Users::ConfirmationsController < Devise::ConfirmationsController 
           before_filter :set_redirect_location, :only => :show
      
           def set_redirect_location
               session["user_return_to"] = params[:redirect_to] if params[:redirect_to]
           end
      end
      

      除此之外,您还必须修改路由以使设计调用您的控制器而不是其自己的确认控制器。

      devise_for :users,
                 :controllers => { :confirmations => 'users/confirmations'}
      

      希望这会有所帮助:)

      注意:代码sn-ps并不完整,只包含相关细节。

      【讨论】:

      • 感谢您的反馈。我更喜欢你的 before_filter 实现。我已经完成了我的自定义控制器。而且我知道当他们单击链接时将是两个不同的会话,这就是为什么我想访问它并将其放在 params[] 中以获取确认链接。这样它将超越会话。但是我在首先获取网址时遇到了麻烦。 session['user_redirect_to'] 似乎不起作用。有什么建议吗?
      【解决方案3】:

      从我从设计源代码中的 cmets 中可以看出,您需要做的就是在您的 registrations_controller.rb 中实现以下内容:

      def after_inactive_sign_up_path_for(resource_or_scope)
        session["user_return_to"]
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多