【问题标题】:Add api key to every request in ActiveResource为 ActiveResource 中的每个请求添加 api 密钥
【发布时间】:2010-05-27 04:16:58
【问题描述】:

我有 2 个 RESTful Rails 应用程序正在尝试相互交流。两者都是用 Rails 3(目前是 beta3)编写的。对服务的请求将需要使用 api 密钥,这只是每个请求都需要的参数。我似乎找不到任何有关如何执行此操作的信息。

您通过 site= 方法定义资源连接到的 url。应该有等效的 query_params= 方法或类似方法。

我发现一篇与此相关的好博文来自 2008 年 10 月,因此对 Rails 3 并不完全有用。

更新:我有一个想法。小型 Rack 中间件或 Metal 会是解决这个问题的答案吗?它可以通过请求,将它的 api_key 附加上。

【问题讨论】:

  • 我也有同样的问题。你找到解决办法了吗?
  • 抱歉 Nicolas,不,我没有找到解决方案。我认为答案是在每个请求中手动分配 api 密钥。
  • @Nicolas 我知道这个问题比较老,但看看我的解决方案是否符合您的要求。
  • 感谢开尔文。您的解决方案与我所寻找的最接近。

标签: ruby-on-rails ruby ruby-on-rails-3 activeresource


【解决方案1】:

使用 model#prefix_options 将参数传递到查询字符串的哈希值(或者甚至作为 Model.prefix 部分的替代,例如“/myresource/:param/”将替换为 prefix_options[:param ] 。在前缀中找不到的任何哈希键都将添加到查询字符串中,这就是我们在您的情况下想要的)。

class Model < ActiveResource::Base
  class << self
    attr_accessor :api_key
  end

  def save
    prefix_options[:api_key] = self.class.api_key
    super
  end
end

Model.site = 'http://yoursite/'
Model.api_key = 'xyz123'
m = Model.new(:field_1 => 'value 1')
# hits http://yoursite:80/models.xml?api_key=xyz123
m.save

【讨论】:

  • 这就是我要说的。我想你可以打开 ActiveResource::Base 并添加这些方法,然后它会自动发生在所有请求上。
  • 这只会修复save。 @benny-degezelle 下面建议的唯一解决方案是在全球范围内处理它
  • m = Model.new m.prefix_options[:api_key] = 'xyz123'
【解决方案2】:

我最近遇到了类似的问题,如果您使用的是 Rails3,它支持使用自定义标头,这使这些情况的生活变得更加轻松。

在您提出请求的一方,添加

headers['app_key'] = 'Your_App_Key'

到你从 ActiveResource::Base 继承的类

在你是服务器上,对于身份验证,只需将其接收为

request.headers['HTTP_APP_KEY']

例如:

class Magic < ActiveResource::Base
    headers['app_key'] = 'Your_App_Key'
end

现在 Magic.get、Magic.find、Magic.post 都会发送 app_key

【讨论】:

  • 我用了一个带下划线的词_,但它不起作用。当我更改为- 时,它确实如此。我希望这可以为某人节省一些时间。奇怪的是,我记得它在开发中与 _ 一起使用,但在生产中发生了错误。
【解决方案3】:

我有更好的解决方案!我尝试在中间件中使用 Rack,但我没有找到任何解决方案....

我建议你使用这个模块来覆盖 ActiveReouse::Base 的方法

将此库添加到 /lib/active_resource/extend/ 目录中,不要忘记取消注释
"config.autoload_paths += %W(#{config.root}/lib)" in config/application.rb

module ActiveResource #:nodoc:
  module Extend
    module AuthWithApi
      module ClassMethods
        def element_path_with_auth(*args)
          element_path_without_auth(*args).concat("?auth_token=#{self.api_key}")
        end
        def new_element_path_with_auth(*args)
          new_element_path_without_auth(*args).concat("?auth_token=#{self.api_key}")
        end
        def collection_path_with_auth(*args)
          collection_path_without_auth(*args).concat("?auth_token=#{self.api_key}")
        end
      end

      def self.included(base)
        base.class_eval do
          extend ClassMethods
          class << self
            alias_method_chain :element_path, :auth
            alias_method_chain :new_element_path, :auth
            alias_method_chain :collection_path, :auth
            attr_accessor :api_key
          end
        end
      end  
    end
  end  
end

在模型中

class Post < ActiveResource::Base
  include ActiveResource::Extend::AuthWithApi

  self.site = "http://application.localhost.com:3000/"
  self.format = :json

  self.api_key = 'jCxKPj8wJJdOnQJB8ERy'

  schema do
    string  :title
    string  :content
  end

end

【讨论】:

  • 我创建了一个类,我想将其用作所有资源的“基础”,但如果我将 api_key 放入其中,子类将看不到它。有什么建议吗?
【解决方案4】:

根据 Joel Azemar 的回答,但我必须做出一些改变。 首先,在我使用的活动资源gem(2.3.8)中,没有'new_element_path',所以别名显然失败了.. 其次,我更新了将令牌添加到查询的方式,因为如果您自己添加更多参数,它就会中断。例如。请求http://example.com/api/v1/clients.xml?vat=0123456789?token=xEIx6fBsxy6sKLJMPVM4(注意?token= i.o. &token=)

这是我更新的 sn-p auth_with_api.rb;

module ActiveResource #:nodoc:
  module Extend
    module AuthWithApi
      module ClassMethods
        def element_path_with_auth(id, prefix_options = {}, query_options = nil)
          query_options.merge!({:token => self.api_key})
          element_path_without_auth(id, prefix_options, query_options)
        end
        def collection_path_with_auth(prefix_options = {}, query_options = nil)
          query_options.merge!({:token => self.api_key})
          collection_path_without_auth(prefix_options, query_options)
        end
      end

      def self.included(base)
        base.class_eval do
          extend ClassMethods
          class << self
            alias_method_chain :element_path, :auth
            # alias_method_chain :new_element_path, :auth
            alias_method_chain :collection_path, :auth
            attr_accessor :api_key
          end
        end
      end  
    end
  end
end

【讨论】:

    【解决方案5】:

    活动资源当前没有将 api 密钥传递给远程服务的好方法。将 api_key 作为参数传递会将其添加到远程服务上的对象属性中,我认为这不是您想要的行为。这当然不是我需要的行为

    【讨论】:

      【解决方案6】:

      我建议您有一个继承自 ActiveResource::Base 的基类并覆盖 self.collection_pathself.element_path 类方法以始终注入 API KEY,例如:

      class Base < ActiveResource::Base
        def self.collection_path(prefix_options = {}, query_options = {})
         super(prefix_options, query_options.merge(api_key: THE_API_KEY))
        end
      
        def self.element_path(id, prefix_options = {}, query_options = {})
          super(id, prefix_options, query_options.merge(api_key: THE_API_KEY))
        end
      end
      
      class User < Base; end
      
      User.all # GET /users/?api_key=THE_API_KEY
      

      这将始终在您的 ActiveResource 方法调用中注入您的 API_KEY。

      【讨论】:

        【解决方案7】:

        一个活动资源对象的行为很像一个(简化的)活动记录对象。 如果您希望传递一个新参数,则可以通过将其添加为属性来将其设置在 AR 对象上。例如:

        jane = Person.create(:first => 'Jane', :last => 'Doe', :api_key => THE_API_KEY)
        

        它应该将 api_key 作为参数传递,以及所有其他参数。

        【讨论】:

          猜你喜欢
          • 2014-05-12
          • 1970-01-01
          • 2018-07-29
          • 1970-01-01
          • 2011-12-22
          • 2019-05-02
          • 1970-01-01
          • 1970-01-01
          • 2012-02-03
          相关资源
          最近更新 更多