我有一个具有不同方法的类,但在这些方法上,我需要在执行一些调用之前检查访问令牌
class SomeClass
def initialize
@client = SomeModule::Client.new
end
def get_intervention_chart(subId:, projectId:, interventionId:)
@client.check_presence_of_access_token()
SomeModule::Service::Project.new(@client).get_intervention_chart(subId: subId, projectId: projectId, interventionId: interventionId)
end
def get_intervention_documents(subId:, projectId:, interventionId:)
@client.check_presence_of_access_token()
SomeModule::Service::Project.new(@client).get_intervention_documents(subId: subId, projectId: projectId, interventionId: interventionId)
end
end
如您所见,我调用了“check_presence_of_access_token”方法,该方法检查访问令牌是否存在,如果不存在,则获取另一个令牌并将其存储在文件中。
这是我的客户机类:
class Client
class Configuration
attr_accessor :access_token
attr_reader :access_token_path, :endpoint, :client_id, :client_secret, :subId
def initialize
@access_token = ''
@access_token_path = Rails.root.join('tmp/connection_response.json')
@endpoint = ENV['TOKEN_ENDPOINT']
@client_id = ENV['CLIENT_ID']
@client_secret = ENV['CLIENT_SECRET']
@subId = "SOME_ID"
end
end
def initialize
@configuration = Configuration.new
end
# Check if the file 'connection_response' is present and if the token provided is still valid (only 30 min)
def check_presence_of_access_token
if File.exist?(self.configuration.access_token_path.to_s)
access_token = JSON.parse(File.read(self.configuration.access_token_path.to_s))["access_token"]
if access_token
jwt_decoded = JWT.decode(access_token, nil, false).first
# we want to check if the token will be valid in 60s to avoid making calls with expired token
if jwt_decoded["exp"] > (DateTime.now.to_i + 60)
self.configuration.access_token = access_token
return
end
end
end
get_token()
end
def get_token
config_hash = Hash.new {}
config_hash["grant_type"] = "client_credentials"
config_hash["client_id"] = self.configuration.client_id
config_hash["client_secret"] = self.configuration.client_secret
response = RestClient.post(self.configuration.endpoint, config_hash, headers: { 'Content-Type' => 'application/x-www-form-urlencoded' })
response_body = JSON.parse(response.body)
self.configuration.access_token = response_body["access_token"]
stock_jwt(response_body.to_json)
end
def stock_jwt(response_body)
File.open(self.configuration.access_token_path.to_s, 'w+') do |file|
file.write(response_body)
end
end
end
我不知道如何重构这个,你能帮我吗?
1条答案
按热度按时间e5nszbig1#
oo语言的一般原则是“懒惰”,并尽可能推迟决策。我们将使用该原则重构代码,使令牌在到期时自动刷新自身,和/或在尚未刷新时自动获取自身。
还有一组原则统称为固体。我们也将使用这些原则。
关于方法和模块,我要提到的最后一个原则是“越小越好”。再加上solid中的“s”(单一职责),您将看到重构包含更多但更小的方法。
撇开原则不谈,从问题陈述中不清楚代币是短期的(只持续一个“会话”)还是长期的(例如:持续时间比单个会话更长)。
如果令牌是长寿命的,那么将其存储到文件中是可以的,只要使用它的进程在同一个系统上。
如果多个web服务器将使用此代码,那么除非每个服务器都有自己的令牌,否则应使用某种数据存储(如redis、mysql或postgres)在所有系统中共享令牌。
由于您的代码使用的是一个文件,我们将假定同一系统上的多个进程可能正在共享该令牌。
根据这些原则和假设,下面是对代码的重构,使用文件存储令牌,使用“惰性”延迟模块化逻辑。
那么,这是如何工作的呢?
假设客户端代码使用
token
,只是评估token
方法将导致检索或刷新未过期的令牌(如果存在),或获取新令牌(如果不存在)。因此,在使用前不需要“检查”令牌
@client
连接。当@client
连接使用令牌,正确的事情就会发生。不会更改的值被缓存,以避免重做生成它们的逻辑。例如:不需要反复解码jwt字符串。
当当前令牌时间到期时
token_expired?
将变为true,导致其调用者返回nil,导致该调用者获取new_token
,然后将其存储。这些小方法的最大优点是,每个方法都可以独立测试,因为它们都有一个非常简单的目的。
祝你的项目好运!