我正在做我的第一个rails api服务器。
我已经有了一个控制器,用于我的 User
模型,看起来是这样的。
class UsersController < ApplicationController
def index
if current_user.admin?
@users = User.all
render json: @users
else
render json: { message: 'You do not have the appropriate permissions to access this resource' }, status: 401
end
end
def show
if User.exists?(@id)
@id = params[:id]
if current_user.id.to_s == @id || current_user.admin?
@user = User.find(@id)
render json: @user
else
render json: { message: 'You do not have the appropriate permissions to access this resource' }, status: 401
end
else
render json: { message: 'Requested resource not found' }, status: 404
end
end
end
我想要的是,目前这两个控制器的方法是:
/users
只有当发出请求的认证用户是角色时才会获取所有用户 admin
/users/:id
读取用户 id
只有当发出请求的认证用户有一个匹配的 id
或有作用 admin
当前的实现打破了DRY理念。原因是处理请求用户是否有权限访问请求的资源的逻辑在两个控制器方法中重复。此外,任何模型的控制器方法 show
将重复检查请求的资源是否存在的逻辑。我也觉得这种实现方式会让控制器变得很胖,而我更希望它们是瘦的。
我想从社区和那些曾经解决过这个问题的人那里了解到的是;为了符合DRY的理念,让控制器瘦下来,最好的方法是什么。
你需要使用一些授权宝石,如 cancancan
. 这正是你所需要的。而且它的 else
不 elsif
. elsif
后面是条件。
您可以使用 github.comvarvetpundit 代替,用于授权。
它与控制器相匹配,你可以用它来将授权移到另一个类中,而不是将授权放在控制器中。
我已经在多个RailsRails-API项目中使用了这个方法,至今没有遇到问题。
与其写上面的代码。你可以用这个来代替。
另外,为了可读性,优先使用早期返回而不是嵌套的if。
在你的控制器中。
class UsersController < ApplicationController
def index
authorize User # This will call the policy that matches this controller since this is UsersController it will call `UserPolicy`
@users = User.all
render :json => @users
end
def show
@user = User.find_by :id => params[:id] # Instead of using exists which query the data from db then finding it again, you can use find_by which will return nil if no records found.
if @user.blank?
return render :json => {:message => 'User not found.'}, :status => 404
end
authorize @user # This will call the policy that matches this controller since this is UsersController it will call `UserPolicy`
render :json => @user
end
end
在你的Policy中
class UserPolicy < ApplicationPolicy
def index?
@user.admin? # The policy is called in controller then this will check if the user is admin if not it will raise Pundit::NotAuthorizedError
end
def show?
@user.admin? || @record == @user # The policy is called in controller then this will check if the user is admin or the user is the same as the record he is accessing if not it will raise Pundit::NotAuthorizedError
end
end
在您的ApplicationController中
class ApplicationController < ActionController::API
include Pundit
rescue_from Pundit::NotAuthorizedError, :with => :show_forbidden
private
def show_forbidden exception
return render :json => {
:message => 'You are not authorized to perform this action.'
}, :status => 403
end
end