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 调用是异步的,一旦完成,它会调用 onFailureonResponse,具体取决于请求是否失败。在 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
    }