【问题标题】:How to handle before filter for specific action in Grape?Grape中特定动作的过滤前如何处理?
【发布时间】:2016-10-17 14:08:15
【问题描述】:

我在我的 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 之外的所有操作都需要用户登录/验证。创建一个新的端点也没有意义,比如说passwords 并在那里删除password/forget,从逻辑上讲,这个端点应该与用户资源相关。

问题在于 Grape before 过滤器没有像 except, only 这样的选项,我可以说将过滤器应用于某些操作。

您通常如何以干净的方式处理此类案件?

【问题讨论】:

    标签: ruby-on-rails ruby grape grape-api


    【解决方案1】:

    我能想到的一种方法是使用route_setting 为您想要绕过身份验证的路由添加自定义属性。在调用 authenticate! 之前,在 before 过滤器中检查这些属性。像下面这样的东西应该可以工作:

    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
    

    【讨论】:

    • 我比接受的答案更喜欢这个。命名空间方法意味着之前的操作必须存在于嵌套的命名空间内。这将允许在 api 库中注册它,并且只在选择的几个端点上排除它,可能在不同的文件中。
    • before { authenticate! unless route.settings.dig(:auth, :disabled) } 这样会短一点:)
    【解决方案2】:

    使用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! }

    【讨论】:

    • upvoted.. 我想知道现在是否有更优雅的方法
    • 只在端点类中直接工作。如果您想对特定的mount API 执行回调之前的操作,则它不起作用。例如我想运行它得到mount Admin::Base,但我不希望它在Public::Base上运行
    【解决方案3】:

    更清楚一点但可能不太明显的方法是将命名空间拆分为单独的子类:

    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
    

    【讨论】:

      猜你喜欢
      • 2022-10-23
      • 2015-07-07
      • 1970-01-01
      • 2012-10-05
      • 1970-01-01
      • 2019-02-02
      • 2021-10-15
      • 1970-01-01
      相关资源
      最近更新 更多