我正在开发一个与 SoundCloud 交互的应用程序,当我尝试保存我从服务器返回的exchange_token时遇到了问题(除其他外(,我真的可以使用一些帮助。
根据我得到的错误:
undefined method `merge!' for nil:NilClass
问题显然出在我的 sclouds_controller.rb 文件中的第 10 行(包括在下面(:
soundcloud_client.exchange_token(:code => params[:code])
这是在我正在使用的 SoundCloud gem 中调用一个方法。以下是 SoundCloud gem 中产生错误的行:
params.merge!(client_params)
可以在以下方法的第 23 行找到(取自 SoundCloud gem 中的 client.rb 文件(:
def exchange_token(options={})
store_options(options)
raise ArgumentError, 'client_id and client_secret is required to retrieve an access_token' if client_id.nil? || client_secret.nil?
client_params = {:client_id => client_id, :client_secret => client_secret}
params = if options_for_refresh_flow_present?
{
:grant_type => 'refresh_token',
:refresh_token => refresh_token,
}
elsif options_for_credentials_flow_present?
{
:grant_type => 'password',
:username => @options[:username],
:password => @options[:password],
}
elsif options_for_code_flow_present?
{
:grant_type => 'authorization_code',
:redirect_uri => @options[:redirect_uri],
:code => @options[:code],
}
end
params.merge!(client_params)
response = handle_response(false) {
self.class.post("https://#{api_host}#{TOKEN_PATH}", :query => params)
}
@options.merge!(:access_token => response.access_token, :refresh_token => response.refresh_token)
@options[:expires_at] = Time.now + response.expires_in if response.expires_in
@options[:on_exchange_token].call(*[(self if @options[:on_exchange_token].arity == 1)].compact)
response
end
但是,如果我像这样在我的 sclouds_controller.rb 文件中抛出"加薪":
def connected
if params[:error].nil?
raise
soundcloud_client.exchange_token(:code => params[:code])
然后,在控制台中,手动粘贴以下行:
soundcloud_client.exchange_token(:code => params[:code])
我得到以下回复(对我来说,这似乎是成功的(:
$ #<SoundCloud::HashResponseWrapper access_token="xxxxx" expires_in=21599 refresh_token="xxxxx" scope="*">
知道为什么会这样吗?我试图了解我做错了什么,特别是因为我不确定我是否以正确的方式去做这件事。以下是我的一些代码,用于提供更多上下文。提前感谢!
sclouds_controller.rb:
class ScloudsController < ApplicationController
before_action :authenticate_user!, only: [:connect, :connected]
def connect
redirect_to soundcloud_client.authorize_url(:display => "popup")
end
def connected
if params[:error].nil?
soundcloud_client.exchange_token(:code => params[:code])
unless user_signed_in?
flash[:alert] = 's'
redirect_to :login
end
current_user.update_attributes!({
:soundcloud_access_token => soundcloud_client.access_token,
:soundcloud_refresh_token => soundcloud_client.refresh_token,
:soundcloud_expires_at => soundcloud_client.expires_at
})
end
redirect_to soundcloud_client.redirect_uri
end
def disconnect
login_as nil
redirect_to root_path
end
private
def soundcloud_client
return @soundcloud_client if @soundcloud_client
@soundcloud_client = User.soundcloud_client(:redirect_uri => 'http://localhost:3000/sclouds/connected/')
end
end
用户.rb:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessor :soundcloud_access_token, :soundcloud_refresh_token, :soundcloud_expires_at
has_one :scloud
@SOUNDCLOUD_CLIENT_ID = 'xxxxx'
@SOUNDCLOUD_CLIENT_SECRET = 'xxxxx'
@REDIRECT_URI = 'xxxxx'
def self.soundcloud_client(options={})
options = {
:client_id => @SOUNDCLOUD_CLIENT_ID,
:client_secret => @SOUNDCLOUD_CLIENT_SECRET,
:redirect_uri => @REDIRECT_URI
}.merge(options)
Soundcloud.new(options)
end
def soundcloud_client(options={})
client = self.class.soundcloud_client(options)
options= {
:access_token => soundcloud_access_token,
:refresh_token => soundcloud_refresh_token,
:expires_at => soundcloud_expires_at
}.merge(options)
client.on_exchange_token do
self.update_attributes!({
:soundcloud_access_token => client.access_token,
:soundcloud_refresh_token => client.refresh_token,
:soundcloud_expires_at => client.expires_at
})
end
client
end
end
options= {
:access_token => soundcloud_access_token,
:refresh_token => soundcloud_refresh_token,
:expires_at => soundcloud_expires_at
}.merge(options)
这是如何工作的?
您正在尝试设置options
本地 var(这是方法中 options
参数的副本(,然后您正在尝试合并options
?对我来说看起来像一个自我参照循环...
方法
其次,您提到错误是由以下行引起的:
exchange_token(:code => params[:code])
我在您的文档中看不到此方法?merge!
方法显然是在此方法中的某个地方引起的 - 我们需要知道它被调用的位置,以便我们可以修复它!
我可以看到一个提到merge
,我在上面已经查询过了
更新
感谢您发布代码!
我认为@mischa
是对的 - 但我会保留这段代码来向您展示我会看的内容。
我只能给出一个意见,因为我以前从未使用过这个 gem -gem 代码中有两个调用merge!
:
params.merge!(client_params)
@options.merge!(:access_token => response.access_token, :refresh_token => response.refresh_token)
我会看看这个:
参数
参数哈希在本地填充,如下所示:
params = if options_for_refresh_flow_present?
{
:grant_type => 'refresh_token',
:refresh_token => refresh_token,
}
elsif options_for_credentials_flow_present?
{
:grant_type => 'password',
:username => @options[:username],
:password => @options[:password],
}
elsif options_for_code_flow_present?
{
:grant_type => 'authorization_code',
:redirect_uri => @options[:redirect_uri],
:code => @options[:code],
}
end
这可能是问题的原因,因为它填充了elsif
(而不是else
(。这意味着您必须确保将正确的代码传递给系统
@options
引用@options
但未声明
我想它是在 gem 代码的另一部分声明的,这意味着您需要确保它在那里。这可能会在 gem 初始化时设置(即当您在 SC 和您的应用程序之间设置身份验证时(
如果您没有正确设置@options
,则可能意味着您没有正确调用 gem,从而导致错误。
尝试mischa
的修复程序,看看是否可以为您解决问题。
初始 化
需要注意的事项 - 在系统中包含 gem 的约定是使用initializer
如果您设置了一个初始值设定项来创建一个名为 SOUNDCLOUD 的新常量,则可以在整个应用程序中引用该常量(修复您在此处遇到的任何错误(
如果你愿意,我可以为你写一些代码
@RickPeck非常接近,因为问题实际上出在这段代码摘录中:
params = if options_for_refresh_flow_present? { :grant_type => 'refresh_token', :refresh_token => refresh_token, } elsif options_for_credentials_flow_present? { :grant_type => 'password', :username => @options[:username], :password => @options[:password], } elsif options_for_code_flow_present? { :grant_type => 'authorization_code', :redirect_uri => @options[:redirect_uri], :code => @options[:code], } end
@options
填充在行store_options(options)
中。所以问题是options_for_refresh_flow_present?
、options_for_credentials_flow_present?
和options_for_code_flow_present?
都不是真的。
的相关选项是代码流:
def options_for_code_flow_present? !!(@options[:code] && @options[:redirect_uri]) end
期望options
同时拥有:code
和:redirect_uri
.在您的代码中,您只传递 :code
.添加一个:redirect_uri
,你应该很高兴。
@Mischa建议的内容可能会为您解决此问题,因为当您设置它时,您的:redirect_uri
nil
......