【问题标题】:Ktor seems to be swallowing exceptionsKtor 似乎正在吞噬异常
【发布时间】:2020-08-27 17:49:47
【问题描述】:

我将 Ktor 与 kotlin 多平台一起使用,我试图弄清楚为什么我没有收到任何抛出的异常。在我的客户端配置中,我使用HttpResonseValidator 检查返回的状态代码

private val client = HttpClient(clientEngine) {
        install(JsonFeature) {
            serializer = KotlinxSerializer(Json.nonstrict)
        }
//        addDefaultResponseValidation()
        HttpResponseValidator{

            validateResponse { response: HttpResponse ->
                val statusCode = response.status.value
                when (statusCode) {
                    in 300..399 -> throw RedirectResponseException(response)
                    in 400..499 -> throw ClientRequestException(response)
                    in 500..599 -> throw ServerResponseException(response)
                }

                if (statusCode >= 600) {
                    throw ResponseException(response)
                }
            }

            handleResponseException { cause: Throwable ->
                throw cause
            }
        }
    }

我在我的服务器上返回 http 状态代码 401 错误以进行测试,所以我应该看到我的代码抛出一个 ClientRequestException 并且 validateResponse 确实被调用,但我从未在控制台和我的应用程序中看到任何异常在没有任何迹象的情况下停止。

这是我的电话

private suspend fun getDataForUrl(url:String, callback: (MyObject?) -> Unit){
    val response = client.get<MyObject>(url)
    callback(response)
}

调用方式

fun getData(callback: (MyObject?) -> Unit){
    launch(dispatcher()){
        getDataForUrl("$BASE_URL", callback)
    }
}

当我用 try/catch 包围通话时

try{
    val response = client.get<MyObject>(url)
catch(e:Exception){
}

我确实得到了异常,但我真的不喜欢它被捕获在这里而不是在我的代码的上层。

当周围没有 try/catch 时,为什么它会被吞没?

【问题讨论】:

  • dispatcher() 到底是做什么的?
  • @Lucho For android 就是这个internal actual fun dispatcher(): CoroutineDispatcher = Dispatchers.IO
  • 好吧,我在 atm 看不到任何东西,但是您可以尝试删除验证器以检查它是否可以在没有它的情况下运行,然后让其余的(我相信 Ktor 默认会在 400、500 上抛出异常)系列,不确定300)。那它会扔给你吗?
  • 我之前没有验证器,但仍然没有出现异常,我使用的是addDefaultResponseValidation,这是我为验证器复制大部分代码的地方,但我仍然没有得到任何东西
  • 我刚刚试了一下,得到了FATAL EXCEPTION。所以我确信问题不是 Ktor,而且“非常”肯定它是关于你如何在这里运行你的协程。您使用 try/catch 看到它的原因是因为您仍在父协程上下文中实际捕获异常并打印它。因此,我对您的建议是,在运行基本上将在协程中运行的 MPP 代码时,请确保您从 Activity/fragment 中“启动”它,以便它一直“冒泡”,这意味着您从 Android 调用的函数是suspend.

标签: ktor kotlin-multiplatform


【解决方案1】:

我的 Kotlin 多平台项目中也有这个 FATAL EXCEPTION

我的 Ktor 方法如下所示:

suspend fun requestToken(userName: String, password: String): AuthResponse {
    return client.post {
        url(urlOauth)
        accept(ContentType.Application.Json)
        body = FormDataContent(Parameters.build {
            append("grant_type", "password")
            append("username", userName)
            append("password", password)
        })
    }
}

如果一切正常,则使用HttpResponseValidator。但是当我强制出错时,例如使用错误的 URL,HttpResponseValidator 不会被使用,并且我的应用程序(在本例中为 iOS 应用程序)会崩溃。

当我打开生成的 Swift 类时,我看到这条注释添加到我的方法中:

/**
 @note This method converts instances of CancellationException to errors.
 Other uncaught Kotlin exceptions are fatal.
*/

这意味着所有其他错误都会导致我的应用程序崩溃。 CancellationExceptionsuspend 有关。

为了防止崩溃并得到错误,我添加了@Throws(CancellationException::class, ResponseException::class)。该方法现在看起来像这样:

@Throws(CancellationException::class, ResponseException::class)
suspend fun requestToken(userName: String, password: String): AuthResponse {
    return client.post {
        url(urlOauth)
        accept(ContentType.Application.Json)
        body = FormDataContent(Parameters.build {
            append("grant_type", "password")
            append("username", userName)
            append("password", password)
        })
    }
}

我就是这样使用它的:

ApiKt.requestToken(userName: "xxx", password: "xxx") { (response: AuthResponse?, error: Error?) in
    if let response = response {
        print("access token: \(response.accessToken)")
    } else if let error = error {
        print("failed to load access token. error: \(error)")
    } else {
        print("Something strange has happened. We have received neither a response nor an error.")
    }
}

当我现在强制出错时,应用程序不会崩溃。相反,我收到一个错误并将其打印出来(在这种情况下)。

最后,我不知道为什么在出现错误时不使用HttpResponseValidator,但这会阻止FATAL EXCEPTION

【讨论】:

    【解决方案2】:

    我遇到了类似的问题,我的自定义 HttpResponseValidator 对于 401 以外的响应状态代码调用得很好。 经过进一步调查,我发现我在 DefaultResponseValidation.kt(由 addDefaultResponseValidation 使用)中的断点被触发了。 我不太明白为什么 validateResponse() 被触发,因为 addDefaultResponseValidation() 在我的代码中从未被调用过。但也许这是使用默认 Http Client 时的默认设置。 然而,状态码 401 会在执行更改之前触发一些默认代码(这将在调用我的验证器之前返回)的想法让我尝试了

    expectSuccess = false
    

    这为我解决了这个问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-23
      • 2011-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多