Rails中Sidekiq Worker的API请求重构及NameError报错解决
问题解决与代码重构方案
报错原因
报错undefined local variable or method 'url'是因为bindable_lead_client和post_request方法试图访问perform方法内的局部变量url,但这两个方法的作用域中不存在该变量,导致变量未定义。
推荐重构方案(抽离API客户端类)
将Active Campaign的HTTP请求逻辑抽离为独立服务类,让Worker专注于业务逻辑,代码更整洁且易于维护。
1. 创建Active Campaign客户端服务类
新建app/services/active_campaign_client.rb:
class ActiveCampaignClient BASE_URL = Rails.application.credentials.dig(:active_campaign, :url) API_KEY = Rails.application.credentials.dig(:active_campaign, :key) def initialize @http_client = build_http_client end def update_contact_field(contact_id, field_id, value) url = URI("#{BASE_URL}/api/3/contacts/#{contact_id}") request = build_put_request(url) request.body = JSON.dump(contact: { fieldValues: [{ field: field_id, value: value }] }) response = JSON.parse(@http_client.request(request).read_body).symbolize_keys handle_response(response, contact_id, value) end private def build_http_client base_uri = URI(BASE_URL) http = Net::HTTP.new(base_uri.host, base_uri.port) http.use_ssl = true http end def build_put_request(url) request = Net::HTTP::Put.new(url) request['Accept'] = 'application/json' request['Content-Type'] = 'application/json' request['api-token'] = API_KEY request end def handle_response(response, contact_id, value) if response[:code] == '200' Rails.logger.info "Successfully updated contact #{contact_id} with lead id #{value}" else raise "Error updating contact: #{response[:body]}" end end end
2. 重构Worker类
class UpdateLeadIdWorker include Sidekiq::Worker def perform(ac_id, current_user_id) lead = Lead.where(user_id: current_user_id).last raise 'Lead not found' unless lead field_id = quote_type_to_field_id(lead.quote_type) ActiveCampaignClient.new.update_contact_field(ac_id, field_id, lead.lead_id) end private def quote_type_to_field_id(quote_type) case quote_type when 'renter' then '5' when 'home' then '4' when 'auto' then '3' else raise "Invalid quote type: #{quote_type}" end end end
重构优势
- 单一职责:Worker仅处理业务逻辑(查询Lead、映射字段ID、调用API),HTTP请求逻辑封装在客户端类中,职责清晰。
- 可复用性:客户端类可在其他Worker或控制器中复用,避免重复代码。
- 易测试:客户端类可单独测试,Worker的测试只需验证业务逻辑的正确性。
- 易维护:新增报价类型时,仅需修改
quote_type_to_field_id方法,无需改动HTTP请求代码。
快速修复方案(不抽离类)
若暂时不想抽离服务类,可通过将url作为参数传递给方法解决作用域问题:
class UpdateLeadIdWorker include Sidekiq::Worker BASE_URL = Rails.application.credentials.dig(:active_campaign, :url) private_constant :BASE_URL API_KEY = Rails.application.credentials.dig(:active_campaign, :key) private_constant :API_KEY def perform(ac_id, current_user_id) lead = Lead.where(user_id: current_user_id).last url = URI("#{BASE_URL}/api/3/contacts/#{ac_id}") https = bindable_lead_client(url) pr = post_request(url) data = { contact: { fieldValues: [{ field: quote_type_to_field_id(lead.quote_type), value: lead.lead_id }] } } pr.body = JSON.dump(data) response = JSON.parse(https.request(pr).read_body).symbolize_keys if response.code == '200' Rails.logger.info "Successfully updated contact #{ac_id} with lead id #{lead.lead_id}" else raise "Error creating contact: #{response.body}" end end def bindable_lead_client(url) http = Net::HTTP.new(url.host, url.port) http.use_ssl = true http end def post_request(url) post_request_ = Net::HTTP::Put.new(url) post_request_['Accept'] = 'application/json' post_request_['Content-Type'] = 'application/json' post_request_['api-token'] = API_KEY post_request_ end private def quote_type_to_field_id(quote_type) case quote_type when 'renter' then '5' when 'home' then '4' when 'auto' then '3' else raise 'Invalid quote type' end end end
内容的提问来源于stack exchange,提问作者theKid
相关产品推荐
相关产品推荐

