Retrofit 响应主体提交返回 null,但日志记录拦截器在 logcat 中显示完整响应
Retrofit response body filed retun null, but logging interceptor showing full response in logact
我正在使用 retrofit2.6.2 进行 api 调用。 LoggingInterceptor 在 logcat 中显示完整响应,但改造响应主体 return 为空。我没有弄清楚我的问题在哪里。
我的 json 数据架构是
{
"error":false,
"msg":"Banner Found",
"id":"9",
"activity":"VipPremium1",
"imageUrl":"https:\/\/1.bp.blogspot.com\/-Kh3RQlJH7Xw\/X-1mIPi7_HI\/AAAAAAAAFME\/Y2bCnU5odngcdDT83uC9QwUr7IGJdTDfACLcBGAsYHQ\/s2616\/COMPRESSED_IMG_1609393684674.jpg",
"actionUrl":"https:\/\/www.youtube.com\/watch?v=ukJX5ZgJec4",
"actionType":1,
"visible":true
}
型号ClassBannerRes
data class BannerRes(
@SerializedName("actionType")
val actionType: Int?,
@SerializedName("actionUrl")
val actionUrl: String?,
@SerializedName("activity")
val activity: String?,
@SerializedName("error")
val error: Boolean?,
@SerializedName("id")
val id: String?,
@SerializedName("imageUrl")
val imageUrl: String?,
@SerializedName("msg")
val msg: String?,
@SerializedName("visible")
val visible: Boolean?
)
Api 界面
@GET("api/helper.getBanner.php")
suspend fun getBanner(
@Query("bannerName") bannerName: String,
): Response<BannerRes>
Api 调用在这里完成
private fun loadPremiumBanner() {
Coroutines.main {
val res = viewModel.getBanner("VipPremium1")
Log.d("Response", res.body()!!.msg!!)
}
}
当我使用
打印响应正文时
Log.d("Response", Gson().toJson(res.body()))
它显示 logcat 中的响应,
Logcat
{"error":false,"msg":"Banner Found","id":"9","activity":"VipPremium1","imageUrl":"https://1.bp.blogspot.com/-Kh3RQlJH7Xw/X-1mIPi7_HI/AAAAAAAAFME/Y2bCnU5odngcdDT83uC9QwUr7IGJdTDfACLcBGAsYHQ/s2616/COMPRESSED_IMG_1609393684674.jpg","actionUrl":"https://www.youtube.com/watch?v\u003dukJX5ZgJec4","actionType":1.0,"visible":true}
但是访问res.body()!!.msg
时显示为null。
改装设置
companion object {
@Volatile
private var myApiInstance: MyApi? = null
private val LOCK = Any()
operator fun invoke() = myApiInstance ?: synchronized(LOCK) {
myApiInstance ?: createClient().also {
myApiInstance = it
}
}
private fun createClient(): MyApi {
val AUTH: String = "Basic ${
Base64.encodeToString(
("${BuildConfig.USER_NAME}:${BuildConfig.USER_PASSWORD}").toByteArray(),
Base64.NO_WRAP
)
}"
val interceptor = run {
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.apply {
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
}
}
val okHttpClient: OkHttpClient = OkHttpClient.Builder()
.callTimeout(10,TimeUnit.SECONDS)
.addInterceptor(interceptor)
.addInterceptor { chain ->
val original: Request = chain.request()
val requestBuilder: Request.Builder = original.newBuilder()
.addHeader("Authorization", AUTH)
.method(original.method, original.body)
val request: Request = requestBuilder.build()
chain.proceed(request)
}
.build()
val gsonBuilder = GsonBuilder()
gsonBuilder.setLenient()
val gson = gsonBuilder.create()
return Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addConverterFactory(ScalarsConverterFactory.create())
.client(okHttpClient)
.build()
.create(MyApi::class.java)
}
}
我认为你不能在 Retrofit 中同时使用 Gson 和 Scalars Converter,因为 retrofit 会混淆包装它。
移除 Scaler(我更喜欢 Gson)并重试。
如果不起作用,则使用 GsonConverterFactory.create()
这个。
使用 Retrofit,您只能使用一次响应 body。因此,第一次调用 body()
将得到 return 响应,但进一步的调用不会。您在记录时正在消耗 body。
我通过添加 kotlin 模型数据 class 成员提交的默认值解决了这个问题。我不知道这背后的原因是什么,
旧数据class
data class BannerRes(
@SerializedName("actionType")
val actionType: Int?,
@SerializedName("actionUrl")
val actionUrl: String?,
@SerializedName("activity")
val activity: String?,
@SerializedName("error")
val error: Boolean?,
@SerializedName("id")
val id: String?,
@SerializedName("imageUrl")
val imageUrl: String?,
@SerializedName("msg")
val msg: String?,
@SerializedName("visible")
val visible: Boolean?
)
修改或数据 class 成员字段默认值解决了我的问题
data class BannerRes(
@SerializedName("error") var error : Boolean = true,
@SerializedName("msg") var msg : String? = null,
@SerializedName("id") var id : String? = null,
@SerializedName("activity") var activity : String? = null,
@SerializedName("imageUrl") var imageUrl : String? = null,
@SerializedName("actionUrl") var actionUrl : String? = null,
@SerializedName("actionType") var actionType : Int = 0,
@SerializedName("visible") var visible : Boolean = false
)
我正在使用 retrofit2.6.2 进行 api 调用。 LoggingInterceptor 在 logcat 中显示完整响应,但改造响应主体 return 为空。我没有弄清楚我的问题在哪里。
我的 json 数据架构是
{
"error":false,
"msg":"Banner Found",
"id":"9",
"activity":"VipPremium1",
"imageUrl":"https:\/\/1.bp.blogspot.com\/-Kh3RQlJH7Xw\/X-1mIPi7_HI\/AAAAAAAAFME\/Y2bCnU5odngcdDT83uC9QwUr7IGJdTDfACLcBGAsYHQ\/s2616\/COMPRESSED_IMG_1609393684674.jpg",
"actionUrl":"https:\/\/www.youtube.com\/watch?v=ukJX5ZgJec4",
"actionType":1,
"visible":true
}
型号ClassBannerRes
data class BannerRes(
@SerializedName("actionType")
val actionType: Int?,
@SerializedName("actionUrl")
val actionUrl: String?,
@SerializedName("activity")
val activity: String?,
@SerializedName("error")
val error: Boolean?,
@SerializedName("id")
val id: String?,
@SerializedName("imageUrl")
val imageUrl: String?,
@SerializedName("msg")
val msg: String?,
@SerializedName("visible")
val visible: Boolean?
)
Api 界面
@GET("api/helper.getBanner.php")
suspend fun getBanner(
@Query("bannerName") bannerName: String,
): Response<BannerRes>
Api 调用在这里完成
private fun loadPremiumBanner() {
Coroutines.main {
val res = viewModel.getBanner("VipPremium1")
Log.d("Response", res.body()!!.msg!!)
}
}
当我使用
打印响应正文时 Log.d("Response", Gson().toJson(res.body()))
它显示 logcat 中的响应, Logcat
{"error":false,"msg":"Banner Found","id":"9","activity":"VipPremium1","imageUrl":"https://1.bp.blogspot.com/-Kh3RQlJH7Xw/X-1mIPi7_HI/AAAAAAAAFME/Y2bCnU5odngcdDT83uC9QwUr7IGJdTDfACLcBGAsYHQ/s2616/COMPRESSED_IMG_1609393684674.jpg","actionUrl":"https://www.youtube.com/watch?v\u003dukJX5ZgJec4","actionType":1.0,"visible":true}
但是访问res.body()!!.msg
时显示为null。
改装设置
companion object {
@Volatile
private var myApiInstance: MyApi? = null
private val LOCK = Any()
operator fun invoke() = myApiInstance ?: synchronized(LOCK) {
myApiInstance ?: createClient().also {
myApiInstance = it
}
}
private fun createClient(): MyApi {
val AUTH: String = "Basic ${
Base64.encodeToString(
("${BuildConfig.USER_NAME}:${BuildConfig.USER_PASSWORD}").toByteArray(),
Base64.NO_WRAP
)
}"
val interceptor = run {
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.apply {
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
}
}
val okHttpClient: OkHttpClient = OkHttpClient.Builder()
.callTimeout(10,TimeUnit.SECONDS)
.addInterceptor(interceptor)
.addInterceptor { chain ->
val original: Request = chain.request()
val requestBuilder: Request.Builder = original.newBuilder()
.addHeader("Authorization", AUTH)
.method(original.method, original.body)
val request: Request = requestBuilder.build()
chain.proceed(request)
}
.build()
val gsonBuilder = GsonBuilder()
gsonBuilder.setLenient()
val gson = gsonBuilder.create()
return Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addConverterFactory(ScalarsConverterFactory.create())
.client(okHttpClient)
.build()
.create(MyApi::class.java)
}
}
我认为你不能在 Retrofit 中同时使用 Gson 和 Scalars Converter,因为 retrofit 会混淆包装它。
移除 Scaler(我更喜欢 Gson)并重试。
如果不起作用,则使用 GsonConverterFactory.create()
这个。
使用 Retrofit,您只能使用一次响应 body。因此,第一次调用 body()
将得到 return 响应,但进一步的调用不会。您在记录时正在消耗 body。
我通过添加 kotlin 模型数据 class 成员提交的默认值解决了这个问题。我不知道这背后的原因是什么,
旧数据class
data class BannerRes(
@SerializedName("actionType")
val actionType: Int?,
@SerializedName("actionUrl")
val actionUrl: String?,
@SerializedName("activity")
val activity: String?,
@SerializedName("error")
val error: Boolean?,
@SerializedName("id")
val id: String?,
@SerializedName("imageUrl")
val imageUrl: String?,
@SerializedName("msg")
val msg: String?,
@SerializedName("visible")
val visible: Boolean?
)
修改或数据 class 成员字段默认值解决了我的问题
data class BannerRes(
@SerializedName("error") var error : Boolean = true,
@SerializedName("msg") var msg : String? = null,
@SerializedName("id") var id : String? = null,
@SerializedName("activity") var activity : String? = null,
@SerializedName("imageUrl") var imageUrl : String? = null,
@SerializedName("actionUrl") var actionUrl : String? = null,
@SerializedName("actionType") var actionType : Int = 0,
@SerializedName("visible") var visible : Boolean = false
)