2

我有一个控制器 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

通常,为了进行身份验证,登录后,我会重定向到 :back。

但是,services#create这里用作 before_filter。在这种情况下,我应该怎么做才能让它回到我的 Posts#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
4

1 回答 1

1

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

考虑使用 session_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 关联。

于 2013-02-27T07:39:52.993 回答