设计 - 确认后重定向页面

Rya*_*nJM 17 devise ruby-on-rails-3

假设用户单击指向受保护页面的链接.然后将它们重定向到登录屏幕,然后登录.如果有,则成功重定向到该页面.但如果他们没有帐户,他们必须注册.事情变得棘手,因为我正在做电子邮件确认.

通过单击链接创建新会话,我无法自动将用户重定向到该受保护页面.我试图通过在确认链接中添加对重定向的引用来更改它.我想要做:

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

但我无法弄清楚如何获取stored_location_for(或者如果这是正确的位置).它是在中定义的Devise::Controllers::Helpers,但它是一个实例方法,所以我不能这样做Devise::Controllers::Helpers.stored_location_for(…).

我的问题是我如何访问stored_location_forOR更好的方法是什么?

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

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

end
Run Code Online (Sandbox Code Playgroud)

这应该工作正常吗?

Rya*_*nJM 12

好吧,我终于明白了.我不确定这与Devise昨天做的更新是否发生了变化,使得Devise :: Mailer将其大部分功能放入模块中(参见此处的代码和此处的票据).

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

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

class User < ActiveRecord::Base
  …
  attr_accessor :return_to
  …
end
Run Code Online (Sandbox Code Playgroud)

然后,您必须在第一次创建用户时设置该变量.我已经有了一个用于注册的自定义控制器设置(请参阅设备自述文件,了解如何设置它,或者查看@ ramc的方向答案).但是做这个部分相对容易,我只是把它添加到参数中,然后让其余部分自行处理.

class RegistrationsController < Devise::RegistrationsController
  def create
    params[:user][:return_to] = session[:user_return_to] if session[:user_return_to]
    …
    super
  end
end
Run Code Online (Sandbox Code Playgroud)

现在用户有一个return_to设置的变量.我们只需要在confirmation_instructions电子邮件中访问它.我已经重写了confirm_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 %>
Run Code Online (Sandbox Code Playgroud)

(对于那些不熟悉的人来说,@ resource是Devise用来定义你的用户的变量).

现在,一旦用户点击该链接,我们就需要重定向它们.@ ramc之前的过滤器效果很好.

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
Run Code Online (Sandbox Code Playgroud)

这将照顾新用户进入受保护页面,然后注册,单击确认链接并正确重定向到受保护页面的情况.

现在我们只需要处理用户执行上述操作的情况,但不是单击链接,而是尝试返回受保护的页面.在这种情况下,他们会被要求注册/登录.他们登录后会被要求确认他们的电子邮件,并可以选择重新发送确认电子邮件.他们输入了他们的电子邮件,现在我们需要将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
Run Code Online (Sandbox Code Playgroud)

与设计相关的唯一不同之处在于创造的第一线.我们传入两个变量.现在我们需要重写该方法.

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
Run Code Online (Sandbox Code Playgroud)

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

而已.

  • 哇,史诗般的帖子! (2认同)

dex*_*ter 10

查看存储方式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
Run Code Online (Sandbox Code Playgroud)

可以使用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
Run Code Online (Sandbox Code Playgroud)

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

devise_for :users,
           :controllers => { :confirmations => 'users/confirmations'}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助 :)

注意:代码段不完整,仅包含相关详细信息.