before_filter与来自另一个控制器的方法

时间:2013-02-27 04:05:38

标签: ruby-on-rails controller before-filter

我有一个控制器PostsController,允许用户在登录前创建帖子。但要保存它,用户必须使用Omniauth登录。

在PostsController中,我有:

class PostsController < ApplicationController
  before_filter :authenticate_user_by_service, :only => :create
  def create
    ...
  end

private
  def authenticate_user_by_service
    redirect_to user_omniauth_authorize_path(:facebook)
  end

现在,我有另一个控制器来处理来自facebook的回调,称为ServicesController

class ServicesController < ApplicationController
  def create
    auth = request.env["omniauth.auth"]
    ... authentication logic here ...
    sign_in(:user, service.user)
  end
  method_alias: :facebook, :create

通常,对于身份验证,登录后,我会重定向到:返回。

但是,此处services#create用作before_filter。在这种情况下,我该怎么做才能将它恢复到我的帖子#create?

更新:我收到这个警告说过滤链在我引用不同的方法时就被中断了

Started POST "/posts" for 127.0.0.1 at 2013-02-26 23:47:41 -0500
Processing by PostsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"H0as8=", "post"=>{"post"=>"bla bla"}, "commit"=>"Create Post"}
Redirected to http://localhost:3000/users/auth/facebook
Filter chain halted as :authenticate_user_by_service rendered or redirected

1 个答案:

答案 0 :(得分:1)

你正在接近这个错误。您正在处理登录并检查某人是否在同一步骤中登录。

考虑使用sessions_controller处理所有注册/登录/注销逻辑,例如:

class SessionsController < ApplicationController
  def new # this will be /login
    session[:return_to] = params[:returnto] unless params[:returnto].nil?
    redirect_to "/auth/facebook"
  end

  def create # this will be the callback after the user is authenticated
    auth_token = request.env["omniauth.auth"]["credentials"]["token"]
    # you'll need to write this based on your app's requirement. 
    # Find a user or create one if he doesn't exist yet.
    user = User.find_or_create_authenticated_user(auth_token) 

    if user.present?
      session[:user_id] = user.id # this stores the current user's id in your session and lets Rails remember him for you.
      redirect_to return_or(products_url) # see below for the usage of return_or
      return
    end

    redirect_to root_url, alert: 'User not found or invalid'
  end

  def destroy # /logout
    session[:user_id] = nil
    redirect_to root_url
  end
end


#routes.rb
match '/logout' => 'sessions#destroy', :as => :logout
match '/login' => 'sessions#new', :as => :login
match '/auth/facebook/callback' => 'sessions#create'

然后,在你的ApplicationController中设置了几个辅助方法:

class ApplicationController < ActionController::Base

  protected  
  # Use this in your views and controllers to get 
  # a handle of the user that is currently logged in. 
  # it will return nil if there is no user logged in.
  def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
  end
  helper_method :current_user

  # Use this to check wether a user is logged in. 
  # returns true if there is a user, false otherwise.
  def logged_in?
    !current_user.nil?
  end
  helper_method :logged_in?

  # Block access to controllers or actions based on 
  # wether there's a user or not.
  def require_login
    unless logged_in?
      # if we need the user to log in, we send him on his way
      # but send him back to the current page afterwards. 
      session[:return_to] = request.fullpath
      redirect_to root_url(subdomain: false), :alert => "Please login"
    end
  end

  # When a user is not logged in, but you send him to log in,
  # you can force him to return to the url he was in or if nothing
  # was set go to a standard path. 
  # See this being set up in SessionsController#new and in require_login and then
  # being used in SessionsController#create
  def return_or(path)
    session.delete(:return_to) || path
  end
  helper_method :return_or
end

这些辅助方法在所有控制器中都可用,因为它们都从ApplicationController继承。然后,您可以告诉您的PostsController发送未登录的用户进入并登录,之后他们会返回到PostsController。

然后解决您在身份验证后仅保存帖子的要求:要么创建帖子,保存帖子,要么在用户通过身份验证后将其更新为公开,或者将帖子的内容存储在会话中在用户通过身份验证后恢复它们:

class PostsController < ApplicationController
  def new
    @post = Post.new(session[:post_params] || {})
  end

  def create
    if logged_in?
      @post = Post.create(params[:post])
      # ... etc
    else
      session[:post_params] = params[:post]
      session[:return_to] = new_post_path
    end
  end
end

请注意,这是一种相当脆弱的方法。我宁愿建议实际创建Post,将其标记为尚未公开,并仅在会话中存储帖子id。验证后,您可以找到post_id,重新创建对象,将其状态设置为public并将其与current_user关联。