【问题标题】:How to handle error response return from server api in kotlin+retrofit+coroutine+mvvm如何在 kotlin+retrofit+coroutine+mvvm 中处理来自服务器 api 的错误响应返回
【发布时间】:2021-10-25 07:14:00
【问题描述】:

我从同一个端点有两个不同的响应。一种是实际成功结果数据模型,一种是错误响应模型。两者的json结构都是这样的:

成功响应:

{
   "result":{
      "id":1,
      "name_en":"Stack Over Flow",
      "summary":"Stack Overflow is the largest, most trusted online community for developers to learn, share​ ​their programming ​knowledge, and build their careers."
   }
}

错误响应:

{
    "message": "Login Failed"
}

我可以处理成功响应,但我无法显示从服务器获得的错误消息。我尝试了很多方法,但我无法做到这一点。 在这里我分享一下我所做的一些方面

MainViewModel.kt

var job: Job? = null
val myDataResponse: MutableLiveData<HandleResource<DataResponse>> =MutableLiveData()

fun myData() {
        job = CoroutineScope(Dispatchers.IO).launch {

            val myDataList = mainRepository.myData()

            withContext(Dispatchers.Main) {
                myDataResponse.postValue(handleMyDataResponse(myDataList))
            }

        }

    }
private fun handleMyDataResponse(myDataResponse: Response<DataResponse>): HandleResource<DataResponse>? {
        if (myDataResponse.isSuccessful) {

            myDataResponse.body()?.let { myDataData ->
                return HandleResource.Success(myDataData)
            }

        }

        return HandleResource.Error(myDataResponse.message())
    }

我需要一个解决方案,而服务器给我错误消息我想在我的前端显示相同的错误消息。我怎样才能做到这一点?

【问题讨论】:

    标签: android kotlin mvvm retrofit2 kotlin-coroutines


    【解决方案1】:
    private fun handleMyDataResponse(myDataResponse: Response<DataResponse>): HandleResource<DataResponse>? {
          
          myDataResponse.body()?.let { myDataData ->    
             if (myDataResponse.code() == 200) {                     
               return HandleResource.Success(myDataData )                 
             } else {
               val rawResponse = myDataData.string()                        
               return HandleResource.Error(getErrorMessage(rawResponse))     
             } 
          }
        }
    
    
    fun getErrorMessage(raw: String): String{   
      val object = JSONObject(raw);
      return object.getString("message");
    }
    

    响应的主体(无论是成功还是失败)是 response.body()。如果你想把它作为一个字符串,然后调用 response.body().string()。由于您想从响应中读取消息对象,因此需要将其转换为 Json。

    【讨论】:

    • 这是我的进程myDataResponse.body()?.let { myDataData -&gt; if (myDataResponse.code() == 200) { return HandleResource.Success(myDataData ) } else { Log.e("Error", "$myDataResponse") Log.e("Error", "${myDataResponse.body()}") } } 我的日志打印此消息:errorBody: Response{protocol=http/1.1, code=403, message=Forbidden, url=apiurl.com/api/v1/data} 但我的实际错误消息是:{"error":"No Data Found"} 但它没有打印出来
    • 服务器错误响应和包含您的自定义错误的服务器成功响应有所不同。在第一种情况下,服务器不返回 http 状态 200,例如 403,在第二种情况下,http 状态为 200,但正文包含对象含义错误。根据您的评论,我可以看到您没有收到状态 200,因此您根本没有从您的 api 收到 {"error":"No Data Found"} - 您最终会在开头提到第一个案例。首先确保您的 API 返回代码 200 以指示成功和不成功的登录。
    • 我的 api 在登录成功时返回 200 代码,在登录失败时返回 403 代码之类的。我尝试了你的第一步,但我已经向你展示了这就是我所得到的。所以请告诉我或解释我如何处理它
    • 抱歉一开始不理解,我更新了回复。希望对您有所帮助。
    • 我很高兴你的谦虚回应。在这里,我与您分享我的要点链接:gist.github.com/ahmed-razeeb/20435963cb6267b34b1ad1dc256cc15b 如果您发现任何问题在您的路上解决了它。感谢您的支持
    【解决方案2】:

    如果您遵循 MVVM 模式,那么我建议为 API 调用创建一个密封类。

    处理api成功和失败或网络问题。 Resource 类将是通用的,因为它将处理所有类型的 api 响应

    sealed class Resource<out T> {
    
        data class Success<out T>(val value: T): Resource<T>()
    
        data class Failure(
            val isNetworkErro: Boolean?,
            val errorCode: Int?,
            val errorBody: ResponseBody?
        ): Resource<Nothing>()
    
    }
    

    在基础仓库上调用API,无论成功还是失败都可以返回资源。

    abstract class BaseRepository {
        suspend fun  <T> safeApiCall(
            apiCall: suspend () -> T
        ): Resource<T>{
            return withContext(Dispatchers.IO){
                try {
                    Resource.Success(apiCall.invoke())
                } catch (throwable: Throwable){
                    when (throwable){
                        is HttpException -> {
                            Resource.Failure(false,throwable.code(), throwable.response()?.errorBody())
                        }
                        else ->{
                            Resource.Failure(true, null, null)
                        }
                    }
                }
            }
        }
    }
    

    如果你遵循这个模式,你将能够处理所有的失败和成功响应,我希望这会有所帮助。

    【讨论】:

    • 这是我的 SelaedClass: 密封类 HandleResource( val data: T? = null, val message: String? = null ) { class Loading : HandleResource() class Success(data: T?) : HandleResource(data) class Error(message: String?, data: T? = null) : HandleResource(data , message) }` 我怎样才能实现你的新想法?
    • 如果不知道如何实现API调用,请参考本项目androidproject
    • 可以为空的布尔值?请不要。
    猜你喜欢
    • 2021-07-26
    • 1970-01-01
    • 1970-01-01
    • 2021-05-28
    • 2020-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多