改造响应主体提交返回 null,但日志拦截器在 logcat 中显示完整响应

Posted

技术标签:

【中文标题】改造响应主体提交返回 null,但日志拦截器在 logcat 中显示完整响应【英文标题】:Retrofit response body filed retun null, but logging interceptor showing full response in logact 【发布时间】:2021-11-25 20:51:29 【问题描述】:

我正在使用 retrofit2.6.2 进行 api 调用。 LoggingInterceptor 在 logcat 中显示完整响应,但改造响应正文返回 null。我没有弄清楚我的问题在哪里。

我的 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

模型类 BannerRes

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)
        


    

【问题讨论】:

你试试不带suspend修饰符? 是的,我已经尝试过不使用挂起功能。但结果相同。 你能发布你的改造设置吗? 我在我的问题中添加了改造设置。请参阅问题末尾。 【参考方案1】:

我通过添加 kotlin 模型数据类成员字段的默认值解决了这个问题。不知道是什么原因,

旧数据类

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?
)

修改或具有成员字段默认值的数据类解决了我的问题

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
    )

【讨论】:

【参考方案2】:

我认为你不能在 Retrofit 中同时使用 Gson 和 Scalars Converter,因为改造会混淆它。 移除 Scaler(我更喜欢 Gson)并重试。 如果不起作用,请使用GsonConverterFactory.create() this。

【讨论】:

起初我只尝试了 Gson 工厂。但同样的问题。和其他 api 工作正常。【参考方案3】:

使用 Retrofit,您只能使用一次响应正文。因此,第一次调用body() 将返回响应,但后续调用不会。当你记录它时,你正在消耗你的身体。

【讨论】:

我删除了日志记录,如果我使用 Gson().toJson(response.body()) 打印响应正文,它工作正常。但是如果只是将响应主体分配给另一个变量并访问它显示为 null 的任何属性。

以上是关于改造响应主体提交返回 null,但日志拦截器在 logcat 中显示完整响应的主要内容,如果未能解决你的问题,请参考以下文章

改造获取请求返回 null

改造服务器响应的默认值显示为空。

WCF 响应不是映射总是返回 null

axios拦截器

改造 2:从响应正文中获取 JSON

改造 API 调用返回 null