可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have 2 RESTful Rails apps I'm trying to make talk to each other. Both are written in Rails 3 (beta3 at the moment). The requests to the service will require the use an api key which is just a param that needs to be on every request. I can't seem to find any information on how to do this.
You define the url the resource connects to via the site= method. There should be an equivalent query_params= method or similar.
There is one good blog post I found related to this and it's from October 2008, so not exactly useful for Rails 3.
Update: I had a thought. Would a small Rack middleware or Metal be the answer to this? It could just pass through the request, tacking it's api_key on.
回答1:
Use model#prefix_options which is a hash for passing params into query string (or even as substitions for parts of the Model.prefix, e.g. "/myresource/:param/" will be replaced by the value of prefix_options[:param] . Any hash keys not found in the prefix will be added to the query string, which is what we want in your case).
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
回答2:
I recently was faced with a similar issue, if you are on Rails3, it supports using custom header which makes life much easier for these situations.
On the side you are making the request from, add
headers['app_key'] = 'Your_App_Key'
to the class you are inheriting from ActiveResource::Base
On you are server, for Authentication, simply receive it as
request.headers['HTTP_APP_KEY']
For Example:
class Magic < ActiveResource::Base
headers['app_key'] = 'Your_App_Key'
end
now Magic.get, Magic.find, Magic.post will all send the app_key
回答3:
I have much nicer solution ! I try with Rack in middleware but i no find any solution in this way....
I propose you this module for override methods of ActiveReouse::Base
Add this lib in /lib/active_resource/extend/ directory don't forget uncomment
"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
in model
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
回答4:
Based on Joel Azemar's answer, but I had to make some changes..
First of all, in the active resource gem I used (2.3.8), there is no 'new_element_path', so aliasing that obviously failed..
Second, I updated the way the token is added to the query, because as was, it would break as soon as you add more params yourself. E.g. request for http://example.com/api/v1/clients.xml?vat=0123456789?token=xEIx6fBsxy6sKLJMPVM4 (notice ?token= i.o. &token=)
Here's my updated snippet 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:
An Active Resource currently has no good way of passing an api key to the remote service. Passing api_key as a parameter will add it to the objects attributes on the remote service, I assume that this is not the behaviour you'd except. It certainly wasn't the behaviour I needed
回答6:
An Active Resource Object behaves much like a (simplified) Active Record object.
If you wish to pass through a new param, then you can set it on the AR object by adding it as an attribute. eg:
jane = Person.create(:first => 'Jane', :last => 'Doe', :api_key => THE_API_KEY)
it should pass the api_key as a parameter, along with all the others.
回答7:
I'd recommend that you have a base class inheriting from ActiveResource::Base
and override the self.collection_path
and self.element_path
class methods to always inject the API KEY something like:
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
This will always inject your API_KEY in your ActiveResource method calls.