【问题标题】:Parsing an error body in a ktor HTTPClient在 ktor HTTPClient 中解析错误正文
【发布时间】:2020-10-26 22:44:46
【问题描述】:

我有一个 api,它在发送错误请求时返回带有正确错误信息的错误正文。例如,我得到状态码 400 和以下正文 -

{
  "errorCode": 1011,
  "errorMessage": "Unable to get Child information"
}

现在,当我为此在多平台模块中编写 ktor 客户端时,我会在响应验证器中捕获它 -

 HttpResponseValidator {
            validateResponse {
                val statusCode = it.status.value
                when (statusCode) {
                    in 300..399 -> print(it.content.toString())
                    in 400..499 -> {
                        print(it.content.toString())
                        throw ClientRequestException(it)
                    }
                    in 500..599 -> print(it.content.toString())
                }
            }
            handleResponseException {
                print(it.message)
            }
        }

我在这里的查询是我无法访问validateResponsehandleResponseException 中的响应错误正文。有没有办法我可以捕获并解析它以获取服务器发送的实际错误?

【问题讨论】:

    标签: kotlin ktor kotlin-multiplatform ktor-client


    【解决方案1】:

    您可以声明一个数据类 Error 来表示您期望的错误响应。

    import kotlinx.serialization.Serializable
    
    @Serializable
    data class Error(
        val errorCode: Int,   //if by errorCode you mean the http status code is not really necessary to include here as you already know it from the validateResponse
        val errorMessage: String
    )
    

    您可以享受暂停的乐趣来解析响应并将其作为错误数据类的实例

     suspend fun getError(responseContent: ByteReadChannel): Error {
        responseContent.readUTF8Line()?.let {
            return Json(JsonConfiguration.Stable).parse(Error.serializer(), it)
        }
        throw IllegalArgumentException("not a parsable error")
    }
    

    然后在handleResponseException里面

    handleResponseException { cause -> 
                val error = when (cause) {
                    is ClientRequestException -> exceptionHandler.getError(cause.response.content)
    // other cases here 
    
                    else -> // throw Exception() do whatever you need 
                }
    //resume with the error 
            }
    

    您可以根据遇到的错误实现一些逻辑,从而引发异常并在代码中的其他位置捕获它 例如

    when (error.errorCode) {
            1-> throw MyCustomException(error.errorMessage)
            else -> throw Exception(error.errorMessage)
        }
    

    希望对你有帮助

    【讨论】:

    • 这似乎无法解析 Json 并因错误 kotlinx.serialization.json.JsonDecodingException: Unexpected JSON token at offset 1: Expected '}' 而中断。 JSON 输入:{
    • @KapilG 确保您的数据类与来自服务器的预期 json 匹配。相应地更改 json 配置以涵盖所有情况(严格、忽略未知键等)。
    • 数据类匹配 JSON。只是当您阅读 ByteReadChannel 时,它只是拾取第一行而不是整个 JSON 并尝试解析它。
    • 我猜主要问题是如何正确读取 byteReadChannel 并且没有太多关于它的文档。
    • 只是为了分享,截至今天,我设法只使用cause.response.readText() 来获取我的api错误文本。
    【解决方案2】:

    以防万一这有助于其他人在这个空间中搜索,在我的 ktor 服务设计中,response.readText 在我的情况下是有意义的:

    try {
    
      httpClient.post...   
    
    } catch(cre: ClientRequestException){
    
      runBlocking {
    
        val content = cre.response?.readText(Charset.defaultCharset())
    
        val cfResponse = Gson().fromJson(content, CfResponse::class.java)
    
        ...
    
      }
    
    }
    

    【讨论】:

      【解决方案3】:

      花了几个小时后,我通过以下步骤得到了错误正文。

      1.为错误定义模型类。就我而言,它类似于

      @Serializable
      data class MyException(
          val message : String,
          val code : String,
          val type : String,
          val status_code : Int
      ) : RuntimeException()
      

      您可以看到我还将自定义类扩展为 RuntimeException,因为我希望我的类表现得像 Exception 类

      2。调用 API

      try {
           val mClient = KtorClientFactory().build()
      
           val res = mClient.post<MemberResponse>("${baseURL}user/login/") {
                      //.....
                     }
                  
           emit(DataState.Success(res))
      
      } catch (ex: Exception) {
          if (ex is ClientRequestException) {
              
              val res = ex.response.readText(Charsets.UTF_8)
              
              try {
                  val myException = Json { ignoreUnknownKeys = true }
                                    .decodeFromString(MyException.serializer(), res)
      
                  emit(DataState.Error(myException))
      
               } catch (ex: Exception) {
                    ex.printStackTrace()
                 }
          } else
               emit(DataState.Error(ex))
      }
      

      就是这样。您已经解析了错误正文。

      要简明扼要地理解它,你只需要重点分两步。

      1. val res = ex.response.readText(Charsets.UTF_8)

      2。 val myException = Json { ignoreUnknownKeys = true }.decodeFromString(MyException.serializer(), res)

      【讨论】:

        猜你喜欢
        • 2020-06-23
        • 1970-01-01
        • 2018-10-08
        • 1970-01-01
        • 2019-10-14
        • 2020-10-07
        • 1970-01-01
        • 2019-05-01
        • 2019-11-04
        相关资源
        最近更新 更多