如何处理 Grape 中特定操作的过滤器之前?
How to handle before filter for specific action in Grape?
我正在我的 Rails 项目中安装 Grape 以构建 RESTful API。
现在一些端点的操作需要身份验证,而另一些则不需要身份验证。
例如,我有 users
端点,看起来像:
module Backend
module V1
class Users < Grape::API
include Backend::V1::Defaults
before { authenticate! }
resource :users do
desc "Return a user"
params do
requires :id, type: Integer, desc: 'User id'
end
get ':id' do
UsersService::Fetch.new(current_user,params).call
end
desc "Update a user"
params do
requires :id, type: Integer, desc: 'User id'
requires :display_name, type: String, desc: 'Display name'
requires :email, type: String, desc: 'Email'
end
post ':id' do
UsersService::Save.new(current_user,params).call
end
desc "Reset user password"
params do
requires :old_password, type: String, desc: 'old password'
requires :password, type: String, desc: 'new password'
end
post 'password/reset' do
PasswordService::Reset.new(current_user,params).call
end
desc "Forget password"
params do
requires :email, type: String
end
post 'password/forget' do
PasswordService::Forget.new(current_user,params).call
end
end
end
end
end
现在如您所见,除 password/forget
之外的所有操作都需要用户为 logged-in/authenticated。创建一个新的端点也没有意义,比如 passwords
并且只是删除 password/forget
从逻辑上讲,这个端点应该与用户资源相关。
问题在于 Grape before
过滤器没有像 except, only
这样的选项,我可以在其中说对某些操作应用过滤器。
一般情况下,您是如何处理得干干净净的?
一种肮脏的帮助方式是使用 namespace
,例如:
module Backend
module V1
class Users < Grape::API
include Backend::V1::Defaults
namespace :users do
desc "Forget password"
params do
requires :email, type: String
end
post 'password/forget' do
PasswordService::Forget.new(current_user,params).call
end
namespace do
before { authenticate! }
desc "Return a user"
params do
requires :id, type: Integer, desc: 'User id'
end
get ':id' do
UsersService::Fetch.new(current_user,params).call
end
desc "Update a user"
params do
requires :id, type: Integer, desc: 'User id'
requires :display_name, type: String, desc: 'Display name'
requires :email, type: String, desc: 'Email'
end
post ':id' do
UsersService::Save.new(current_user,params).call
end
desc "Reset user password"
params do
requires :old_password, type: String, desc: 'old password'
requires :password, type: String, desc: 'new password'
end
post 'password/reset' do
PasswordService::Reset.new(current_user,params).call
end
end
end
end
end
end
这样我们不会 运行 在过滤 users/password/forget
之前,但对于其余的我们 运行 before { authenticate! }
我能想到的一种方法是使用 route_setting
为您想要绕过身份验证的路由添加自定义属性。在调用 authenticate!
之前检查前过滤器中的这些属性。像下面这样的东西应该可以工作:
module Backend
module V1
class Users < Grape::API
include Backend::V1::Defaults
before { authenticate! unless route.settings[:auth] && route.settings[:auth][:disabled] }
resource :users do
desc "Return a user"
params do
requires :id, type: Integer, desc: 'User id'
end
get ':id' do
UsersService::Fetch.new(current_user,params).call
end
desc "Update a user"
params do
requires :id, type: Integer, desc: 'User id'
requires :display_name, type: String, desc: 'Display name'
requires :email, type: String, desc: 'Email'
end
post ':id' do
UsersService::Save.new(current_user,params).call
end
desc "Reset user password"
params do
requires :old_password, type: String, desc: 'old password'
requires :password, type: String, desc: 'new password'
end
post 'password/reset' do
PasswordService::Reset.new(current_user,params).call
end
desc "Forget password"
route_setting :auth, disabled: true
params do
requires :email, type: String
end
post 'password/forget' do
PasswordService::Forget.new(current_user,params).call
end
end
end
end
end
更清楚一点但可能不是很明显的方法是将您的名称空间拆分为单独的子类:
module API
module Users
class Root < Grape::API
namespace :users do
mount Create # <= `before` callback is not executed
before do
authenticate!
end
mount Update # <= `before` callback is executed
end
end
end
end
相对目录结构如下所示:
api/users/root.rb
api/users/create.rb
api/users/update.rb
我正在我的 Rails 项目中安装 Grape 以构建 RESTful API。
现在一些端点的操作需要身份验证,而另一些则不需要身份验证。
例如,我有 users
端点,看起来像:
module Backend
module V1
class Users < Grape::API
include Backend::V1::Defaults
before { authenticate! }
resource :users do
desc "Return a user"
params do
requires :id, type: Integer, desc: 'User id'
end
get ':id' do
UsersService::Fetch.new(current_user,params).call
end
desc "Update a user"
params do
requires :id, type: Integer, desc: 'User id'
requires :display_name, type: String, desc: 'Display name'
requires :email, type: String, desc: 'Email'
end
post ':id' do
UsersService::Save.new(current_user,params).call
end
desc "Reset user password"
params do
requires :old_password, type: String, desc: 'old password'
requires :password, type: String, desc: 'new password'
end
post 'password/reset' do
PasswordService::Reset.new(current_user,params).call
end
desc "Forget password"
params do
requires :email, type: String
end
post 'password/forget' do
PasswordService::Forget.new(current_user,params).call
end
end
end
end
end
现在如您所见,除 password/forget
之外的所有操作都需要用户为 logged-in/authenticated。创建一个新的端点也没有意义,比如 passwords
并且只是删除 password/forget
从逻辑上讲,这个端点应该与用户资源相关。
问题在于 Grape before
过滤器没有像 except, only
这样的选项,我可以在其中说对某些操作应用过滤器。
一般情况下,您是如何处理得干干净净的?
一种肮脏的帮助方式是使用 namespace
,例如:
module Backend
module V1
class Users < Grape::API
include Backend::V1::Defaults
namespace :users do
desc "Forget password"
params do
requires :email, type: String
end
post 'password/forget' do
PasswordService::Forget.new(current_user,params).call
end
namespace do
before { authenticate! }
desc "Return a user"
params do
requires :id, type: Integer, desc: 'User id'
end
get ':id' do
UsersService::Fetch.new(current_user,params).call
end
desc "Update a user"
params do
requires :id, type: Integer, desc: 'User id'
requires :display_name, type: String, desc: 'Display name'
requires :email, type: String, desc: 'Email'
end
post ':id' do
UsersService::Save.new(current_user,params).call
end
desc "Reset user password"
params do
requires :old_password, type: String, desc: 'old password'
requires :password, type: String, desc: 'new password'
end
post 'password/reset' do
PasswordService::Reset.new(current_user,params).call
end
end
end
end
end
end
这样我们不会 运行 在过滤 users/password/forget
之前,但对于其余的我们 运行 before { authenticate! }
我能想到的一种方法是使用 route_setting
为您想要绕过身份验证的路由添加自定义属性。在调用 authenticate!
之前检查前过滤器中的这些属性。像下面这样的东西应该可以工作:
module Backend
module V1
class Users < Grape::API
include Backend::V1::Defaults
before { authenticate! unless route.settings[:auth] && route.settings[:auth][:disabled] }
resource :users do
desc "Return a user"
params do
requires :id, type: Integer, desc: 'User id'
end
get ':id' do
UsersService::Fetch.new(current_user,params).call
end
desc "Update a user"
params do
requires :id, type: Integer, desc: 'User id'
requires :display_name, type: String, desc: 'Display name'
requires :email, type: String, desc: 'Email'
end
post ':id' do
UsersService::Save.new(current_user,params).call
end
desc "Reset user password"
params do
requires :old_password, type: String, desc: 'old password'
requires :password, type: String, desc: 'new password'
end
post 'password/reset' do
PasswordService::Reset.new(current_user,params).call
end
desc "Forget password"
route_setting :auth, disabled: true
params do
requires :email, type: String
end
post 'password/forget' do
PasswordService::Forget.new(current_user,params).call
end
end
end
end
end
更清楚一点但可能不是很明显的方法是将您的名称空间拆分为单独的子类:
module API
module Users
class Root < Grape::API
namespace :users do
mount Create # <= `before` callback is not executed
before do
authenticate!
end
mount Update # <= `before` callback is executed
end
end
end
end
相对目录结构如下所示:
api/users/root.rb
api/users/create.rb
api/users/update.rb