我正在按照以下方式实施Twitter OAuth流:
https://developer.twitter.com/en/docs/authentication/guides/log-in-with-twitter
我收到第一步(oauth / request_token)的响应,该响应具有200个代码,但是响应主体完全为空。
我正在使用Retrofit来调用API,并且已经连接了一个拦截器OkHttpClient来调试响应,如下所示:
val client = OkHttpClient.Builder().also { builder ->
builder.addInterceptor { chain ->
val request = chain.request()
val response = chain.proceed(request)
response
}
}.build()
然后按如下所示设置Retrofit:
Retrofit.Builder()
.baseUrl(TWITTER_AUTH_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
.create(TwitterAuthRetrofit::class.java)
.getRequestToken(
authorizationHeaders
).enqueue(object : Callback<TwitterRequestToken> {
override fun onResponse(call: Call<TwitterRequestToken>, response: Response<TwitterRequestToken>) {
onSuccess(response.body())
}
override fun onFailure(call: Call<TwitterRequestToken>, t: Throwable) {
onFailure()
}
})
当我在拦截器中调试时,可以看到响应成功(200),但是响应主体为空,我认为这导致我的Gson反序列化失败。
在拦截器中调用response.body.contentLength()
的结果为-1。
在拦截器中调用response.code
的结果为200。
这是我尝试反序列化响应主体的模型:
data class TwitterRequestToken(
@SerializedName(value = "oauth_token")
val token: String,
@SerializedName(value = "oauth_token_secret")
val tokenSecret: String,
@SerializedName(value = "oauth_callback_confirmed")
val callbackConfirmed: Boolean
)
请注意,我正在使用@SerializedName
提供响应正文的密钥,而我的属性名称对于我们的应用程序是任意的(我们使用驼峰式大小写)。我使用构建器将GsonConverterFactory
添加到了Retrofit实例中,并且对于其他许多请求,都以相同的方式完成了此操作,之前没有问题。
这是我从API中获得的响应,我正在通过上面的拦截器中的调试来查看响应:
Response{protocol=h2, code=200, message=, url=https://api.twitter.com/oauth/request_token}
这是来自cause
的{{1}}消息,我正在从Retrofit中收到Throwable
回调:
onFailure
有人知道这可能是什么原因吗?
答案 0 :(得分:0)
最后弄清楚了,希望这对以后的人有帮助...
Twitter API的oauth / request_token响应主体未编码为JSON;您将需要从响应缓冲区中读取它。具体来说,在使用Retrofit实现API时,您希望您的Retrofit接口返回ResponseBody
(而不是自定义类),从Retrofit构建器中删除GSON,然后在Retrofit的onResponseCallback
中编写以下代码将缓冲区读取为字符串,然后在&
上拆分字符串以获取每个键val对,然后可以在=
上拆分每个键并确保在构造之前拥有所有3个值您的模型:
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
response.body()?.also { body ->
body.source().readString(Charsets.UTF_8).split('&').map { param ->
param.split('=').let { keyVal ->
keyVal[0] to keyVal[1]
}
}.toMap().let { paramMap ->
val oauthToken = paramMap["oauth_token"]
val oauthTokenSecret = paramMap["oauth_token_secret"]
val oauthCallbackConfirmed = paramMap["oauth_callback_confirmed"]?.toBoolean()
if (oauthToken == null || oauthTokenSecret == null || oauthCallbackConfirmed == null) {
onFailure()
} else {
onSuccess(
TwitterRequestToken(
oauthToken,
oauthTokenSecret,
oauthCallbackConfirmed
)
)
}
}
} ?: onFailure()
}