我有一个控制器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
答案 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关联。