Retrofit 2 Request 适用于 Postman 但不适用于 App
Retrofit 2 Request works in Postman but not in App
我有以下代码可以调用 API 从邮政编码获取地址。
fun getAddressFromPostCode(postCode: String): List<Address>{
val trimmedPostCode = postCode.replace("\s".toRegex(),"").trim()
val dataBody = JSONObject("""{"postcode":"$trimmedPostCode"}""").toString()
val hmac = HMAC()
val hmacResult = hmac.sign(RequestConstants.CSSecretKey, dataBody)
val body = JSONObject("""{"data":$dataBody, "data_signature":"$hmacResult"}""")
val url = RequestConstants.URL
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build()
val api:GetAddressAPIService = retrofit.create(GetAddressAPIService ::class.java)
var myList = emptyList<Address>()
val myCall: Call<GetAddressResponse> = api.getAddress(body)
myCall.enqueue(object : Callback<GetAddressResponse> {
override fun onFailure(call: Call<GetAddressResponse>?, t: Throwable?) {
Log.d("RegistrationInteractor", "Something went wrong", t)
Log.d("RegistrationInteractor", call.toString())
}
override fun onResponse(call: Call<GetAddressResponse>?, response: Response<GetAddressResponse>?) {
// Success response
myList = response!!.body()!!.addresses
}
})
return myList
}
这是我打电话的地方:
interface GetAddressAPIService {
@Headers("Accept: application/json; Content-Type: application/json")
@POST("postcode_search.php")
fun getAddress(@Body dataBody: JSONObject): Call<GetAddressResponse>
}
GetAddressResponse 看起来像这样并且似乎是正确的:
数据 class GetAddressResponse(
验证成功:整数,
有效地址:列表
)
数据体是{"data":{"postcode":"M130EN"},"data_signature":"long data signature"} 当我运行 在 Postman 中我得到一个地址列表,但是当我在应用程序中 运行 它时我得到 200 OK 响应但没有地址。有什么想法吗?
您在这里面临的问题是异步数据获取,而 return 立即从函数获取数据。
在您提供的代码中,myCall.enqueue
调用是异步的,一旦完成,它会调用 onFailure
或 onResponse
,具体取决于请求是否失败。在 getAddressFromPostCode
函数的最后一行,你 return myList
值,在那种情况下它仍然是空的,因为 onResponse
还没有被调用。
我建议您熟悉 coroutines,这将使这段代码在处理异步工作时更易于推理。或者您可以简单地向 getAddressFromPostCode
提供一个回调函数,以便在请求完成或失败时调用。
您的代码正在 returning 但 enqueue
是异步的,因此不能保证在 return 之前发生。如果你读到 enqueue
那里有一个回调,这意味着它会 call
back
就绪后,因为它是一个 HTTP 请求然后需要一些时间并且它在之后完成return.
你有 2 个选择,添加 Callback<GetAddressResponse>
作为参数或任何其他回调作为参数,或者你可以让它暂停。
回调
协程是现在推荐的做事方式,所以这不再被认为是一种好的做法。
fun getAddressFromPostCode(postCode: String, callback: Callback<GetAddressResponse): List<Address>{
...
myCall.enqueue(callback)
}
调用 class 必须实现回调并将其作为参数传递。您可以通过使用 lambda 使 kotlin 方式更丰富:
fun getAddressFromPostCode(postCode: String, callback: (items: List<Address> -> Unit))
...
override fun onResponse(call: Call<GetAddressResponse>?, response: Response<GetAddressResponse>?) {
callback(response...)
}
}
所以这使得调用 class 以这种方式使用它
yourClass.getAddressFromPostCode(postalCode) { -> items
//do your thing with items
}
协程
您可以使用挂起函数将其转换为线性代码:
//make it return optional to know if it was failure
suspend fun getAddressFromPostCode(postCode: String): List<Address>? {
...
return try {
myCall.invoke().result?.body()
} catch (e: Exception) {
null
}
}
然后调用 class 必须像这样使用它:
lifeCycleScope.launch {
val addresses = yourClass.getAddressFromPostCode(postalCode)
//do your thing on the UI with the addresses maybe pass it to an adapter
}
我有以下代码可以调用 API 从邮政编码获取地址。
fun getAddressFromPostCode(postCode: String): List<Address>{
val trimmedPostCode = postCode.replace("\s".toRegex(),"").trim()
val dataBody = JSONObject("""{"postcode":"$trimmedPostCode"}""").toString()
val hmac = HMAC()
val hmacResult = hmac.sign(RequestConstants.CSSecretKey, dataBody)
val body = JSONObject("""{"data":$dataBody, "data_signature":"$hmacResult"}""")
val url = RequestConstants.URL
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build()
val api:GetAddressAPIService = retrofit.create(GetAddressAPIService ::class.java)
var myList = emptyList<Address>()
val myCall: Call<GetAddressResponse> = api.getAddress(body)
myCall.enqueue(object : Callback<GetAddressResponse> {
override fun onFailure(call: Call<GetAddressResponse>?, t: Throwable?) {
Log.d("RegistrationInteractor", "Something went wrong", t)
Log.d("RegistrationInteractor", call.toString())
}
override fun onResponse(call: Call<GetAddressResponse>?, response: Response<GetAddressResponse>?) {
// Success response
myList = response!!.body()!!.addresses
}
})
return myList
}
这是我打电话的地方:
interface GetAddressAPIService {
@Headers("Accept: application/json; Content-Type: application/json")
@POST("postcode_search.php")
fun getAddress(@Body dataBody: JSONObject): Call<GetAddressResponse>
} GetAddressResponse 看起来像这样并且似乎是正确的: 数据 class GetAddressResponse( 验证成功:整数, 有效地址:列表 )
数据体是{"data":{"postcode":"M130EN"},"data_signature":"long data signature"} 当我运行 在 Postman 中我得到一个地址列表,但是当我在应用程序中 运行 它时我得到 200 OK 响应但没有地址。有什么想法吗?
您在这里面临的问题是异步数据获取,而 return 立即从函数获取数据。
在您提供的代码中,myCall.enqueue
调用是异步的,一旦完成,它会调用 onFailure
或 onResponse
,具体取决于请求是否失败。在 getAddressFromPostCode
函数的最后一行,你 return myList
值,在那种情况下它仍然是空的,因为 onResponse
还没有被调用。
我建议您熟悉 coroutines,这将使这段代码在处理异步工作时更易于推理。或者您可以简单地向 getAddressFromPostCode
提供一个回调函数,以便在请求完成或失败时调用。
您的代码正在 returning 但 enqueue
是异步的,因此不能保证在 return 之前发生。如果你读到 enqueue
那里有一个回调,这意味着它会 call
back
就绪后,因为它是一个 HTTP 请求然后需要一些时间并且它在之后完成return.
你有 2 个选择,添加 Callback<GetAddressResponse>
作为参数或任何其他回调作为参数,或者你可以让它暂停。
回调
协程是现在推荐的做事方式,所以这不再被认为是一种好的做法。
fun getAddressFromPostCode(postCode: String, callback: Callback<GetAddressResponse): List<Address>{
...
myCall.enqueue(callback)
}
调用 class 必须实现回调并将其作为参数传递。您可以通过使用 lambda 使 kotlin 方式更丰富:
fun getAddressFromPostCode(postCode: String, callback: (items: List<Address> -> Unit))
...
override fun onResponse(call: Call<GetAddressResponse>?, response: Response<GetAddressResponse>?) {
callback(response...)
}
}
所以这使得调用 class 以这种方式使用它
yourClass.getAddressFromPostCode(postalCode) { -> items
//do your thing with items
}
协程
您可以使用挂起函数将其转换为线性代码:
//make it return optional to know if it was failure
suspend fun getAddressFromPostCode(postCode: String): List<Address>? {
...
return try {
myCall.invoke().result?.body()
} catch (e: Exception) {
null
}
}
然后调用 class 必须像这样使用它:
lifeCycleScope.launch {
val addresses = yourClass.getAddressFromPostCode(postalCode)
//do your thing on the UI with the addresses maybe pass it to an adapter
}