后台:我有一个API驱动的rails 3应用程序,它利用DataMapper(DM)和DM适配器与API进行交互。每个DM模型都有一个相应的REST-ish API端点,就像使用rails scaffolding一样。 API需要各种请求标头,包括API令牌,密钥,ID等。标头与请求的数据无关,它们仅用于授权和跟踪目的。许多这些令牌存储在会话中。我想要一种干净的方法,在请求期间为任何模型提供这些API头。
可能的解决方案:
显而易见的答案是将哈希或其他对象中的标记从控制器传递给模型。控制器操作可能具有以下内容:@user = User.find(params[:id], api_headers)
。
问题是需要覆盖任何模型方法以接受其他api_headers
对象。不计算Rails和DataMapper定义的方法,应用程序模型中已经定义了数百种需要重写的方法。所以我排除了重写,这似乎也不是一个好的解决方案,因为它需要覆盖一些荒谬的DM方法,如上面的User#find
示例。
我可以在DM的基类上捕获任何ArgumentError
并检查最后一个参数是否是api_headers
对象,然后将值设置为实例变量并调用所请求的方法。这个思想练习已经让我在处理可选参数等问题。如果给予足够长的时间,我可能会创建一个功能性的弗兰肯斯坦,应该让我解雇但可能不会。
在应用程序控制器中设置before_filter
以将会话存储的API标头转储到单个ApiHeaders
对象中。然后,任何发出API请求的模型都可以使用所需的API标头获取该单例。
应用程序控制器上的其他after_filter
*会在请求结束时将nil
单例上的所有属性设置为ApiHeaders
,以防止请求之间的标头泄漏。
这是目前我首选的解决方案,但我不认为如果after_filter
未被调用,API标头值可能会转移到其他请求中。我不知道在哪种情况下可能会发生这种情况(可能是在应用程序错误中?)引起了人们的关注。我所知道的是,价值不一定会随着请求而消亡。
删除对DataMapper和自定义API适配器的支持,并手动进行所有API调用,并传递所有必需的API标头。除了我没有时间进行这种级别的重写之外,为什么要使用一个框架,如果你必须抛出一大块来支持自定义身份验证方案呢?
从会话中将这些讨厌的API令牌放入应用程序的内容中,最简单的方法是什么?它们可以随每个API请求一起发送?我希望有一个比上面列出的更好的解决方案。
* after_action
答案 0 :(得分:4)
我使用request_store gem在我的用户模型上设置当前用户和请求信息,这只是对线程本地存储的一个小小的垫片,并进行了一些清理。
这使得我的任何模型都可以通过User类获取信息。我可以在任何需要的地方使用User.current
,User.request
和User.location
。
您的控制器只需在对用户进行身份验证后设置User.current
和User.request
。
示例用户模型:
# models/user.rb
require 'request_store'
class User
def self.current
RequestStore.store[:current_user]
end
def self.current=(user)
RequestStore.store[:current_user] = user
end
def self.request
RequestStore.store[:current_request]
end
def self.request=(request)
# stash the request so things like IP address and GEO-IP based location is available to other models
RequestStore.store[:current_request] = request
end
def self.location
# resolve the location just once per request
RequestStore.store[:current_location] ||= self.request.try(:location)
end
end
答案 1 :(得分:0)
使用Thread.current,该线程从请求传递到模型(请注意,如果在请求内部使用子线程,则此操作将中断)。您可以将要共享的属性存储在cattr_accessor或rails缓存中:
在cattr_accessor中
class YourClass
cattr_accessor :my_var_hash
...
# and in your controller
# set the var
YourClass.my_var_hash = {} if YourClass.my_var_hash.nil?
YourClass.my_var_hash[Thread.current.object_id] = {}
YourClass.my_var_hash[Thread.current.object_id][your_var] = 100
... and in your model
lvalue = YourClass.my_var_hash[Thread.current.object_id][your_var]
请注意,如果您使用此方法,则还需要将哈希值之一作为时间戳,并通过删除旧键b / c来获取一些整理信息,最终您将耗尽所有系统内存如果你不做家务
具有缓存:
# in your controller
@var = Rails.cache.fetch("#{Thread.current.object_id}_var_name") do
return 100 # do your work here to create the var value and return it
end
# in your model
lvalue = Rails.cache.fetch(("#{Thread.current.object_id}_var_name")
然后,您可以将缓存过期时间设置为5分钟,也可以在请求结束时使用通配符清除缓存。