在用户执行某项操作后提示用户登录

Tom*_*man 8 authentication ruby-on-rails

你可以在我的说唱歌词解释网站上做的一件事是"喜欢"解释(一旦你登录):

http://dl.getdropbox.com/u/2792776/screenshots/2010-01-17_1645.png

我想向未登录的用户显示"赞"链接,然后,当未登录的用户点击"赞"时,向他显示一个带有"登录或注册"表单的灯箱(如Digg)/Reddit)

http://dl.getdropbox.com/u/2792776/screenshots/2010-01-17_1650.png

实现这一目标的最佳方法是什么?

目前我正在使用这种方法:

  1. 单击"喜欢"POST /annotations/:id/vote(POST主体指示用户是喜欢还是"不喜欢").
  2. vote注解控制器操作有一个require_user before_filter看起来像这样:

    def require_user
      unless current_user
        store_desired_location
        flash[:notice] = "You'll need to login or register to do that"
        redirect_to login_path # map.login '/login', :controller => 'user_sessions', :action => 'new'
        return false
      end
    end
    
    Run Code Online (Sandbox Code Playgroud)
  3. user_sessions#new 看起来像这样:

    def new
      @user_session = UserSession.new
      respond_to do |format|
        format.html {}
        format.js {
          render :layout => false
        }
      end
    end
    
    Run Code Online (Sandbox Code Playgroud)

问题是重定向似乎无法在javascript上正常工作:

http://dl.getdropbox.com/u/2792776/screenshots/2010-01-17_1700.png

如何让它正确重定向?

此外,这是正确的一般方法吗?另一个想法是当没有登录用户时,在javascript中为"Like"链接附加一个不同的处理程序(但我认为这种方法不能很好地扩展到我想以相同方式处理的其他操作)

EmF*_*mFi 3

这里有一些问题需要克服。

  1. 浏览器通常不允许重定向到 POST 请求。

  2. 如果没有额外的输入,redirect_to 不会保留格式。

  3. 存储位置不保留表单数据。

所有这些问题都可以通过消除重定向来解决。

这是我过去的交接方式:

不是在 required_user 中重定向,而是渲染。如果 before 过滤器重定向或渲染,则挂起的操作将被取消。(也不需要返回 false)。不幸的是,这条路线模糊了控制器的边界。但允许简单的 html 回退,并使其自身变得干燥。

新工作流程的高级视图将是:

  1. 请求注释#vote (POST)
  2. required_user 过滤器失败
  3. 渲染新会话
  4. 将登录信息和原始POST数据提交回annotations#vote (POST)
  5. 投票中的新过滤器捕获会话信息并登录。投票按预期进行。如果登录失败返回3。
  6. 注释#vote 重定向/渲染,因为它应该

首先重新设计 require_user 以呈现 user_sessions#new 模板。

def require_user
  unless current_user
    flash[:notice] = "You'll need to login or register to do that"
    @user_session ||= UserSession.new
    respond_to do |format|
      format.html {render :template => 'user_sessions/new'}
      format.js {
        render :template => 'user_sessions/new', :layout => false
      }
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

@user_session ||= UserSession.new确保我们可以将验证错误返回到表单。

现在我们必须增强您的 user_session#new 模板,以便它可以记住该操作。另外,如果您计划使用灯箱,这应该是由相关 RJS 或 new.html.erb 渲染的部分渲染。

首先,我们创建一个部分来创建隐藏字段,保留在重定向中丢失的 POST 数据:

<% if params[:controller] == "annotations" %>
  <% content_for :old_form do %>
    <%= hidden_field_tag "annotation[song_id]", params[:annotation][:song_id] %>
    <%= hidden_field_tag "annotation[vote]", params[:annotation][:vote] %>
  <% end %>
<% end %>
Run Code Online (Sandbox Code Playgroud)

然后在登录部分中渲染该部分,该部分将占据您的灯箱:

<%= render :partial => vote_form_replica %>

<% url = params[:controller] == "user_sessions ? user_sessions_url : {} %>
<% form_tag @user_session, :url => url do |f| %>
  <%= yield :old_form %>
  <%= f.label :user_name %>
  <%= f.text_field :user_name %>
  <%= f.label :password %>
  <%= f.password_field :password %>
  <%= submit_tag %>
<%end%>
Run Code Online (Sandbox Code Playgroud)

form_tag 中 url 的空哈希看起来像是一个错误,但事实并非如此。它确保将表单数据发布到呈现表单的 url。此时应该是annotations/:id/vote

现在让新过滤器登录。本质上它将执行 UserSessionsController#create 在没有渲染/重定向的情况下执行的操作。以下内容是从 RESTful 身份验证插件复制的。

def authenticate
  self.current_user = User.authenticate(params[:login], params[:password])
  if logged_in?
    if params[:remember_me] == "1"
       current_user.remember_me unless current_user.remember_token?
       cookies[:auth_token] = { :value => self.current_user.remember_token, 
         :expires => self.current_user.remember_token_expires_at }
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

剩下的就是确保过滤器顺序正确。

before_filter :authenticate, :require_user, :only => :vote
Run Code Online (Sandbox Code Playgroud)

注意:如果没有此版本的验证,您可能不会使用此版本的 require_user,因此将它们组合到单个过滤器中是有意义的。

就是这样。这种设置方式允许强大的 DRY 且易于重用的代码。通过将新过滤器放入 ApplicationController,它们可以在任何控制器中使用。从现在开始,将此功能添加到任何其他控制器/操作只需 3 个简单步骤:

  1. 创建一个以 vote_form_replica 部分为模型的新部分。
  2. 将相应的渲染语句添加到新的会话模板中。
  3. 将过滤器应用到您的操作中。