ktor 为某些数据类抛出 NoTransformationFoundException

Posted

技术标签:

【中文标题】ktor 为某些数据类抛出 NoTransformationFoundException【英文标题】:ktor throws for some data classes an NoTransformationFoundException 【发布时间】:2020-03-31 22:51:03 【问题描述】:

我有一个功能分支,我从休息服务加载数据。对于两个请求,这绝对按预期工作,但对于一个请求,我总是得到一个 NoTransformationFoundException。

我正在像这样创建 http 客户端:

private val httpClient =
    HttpClient(OkHttp) 
        install(JsonFeature) 
            serializer = GsonSerializer()
        
    

在上面你看到我正在使用带有 gson 的 okhttp。只是为了让问题更容易调试,这里是我的 ktor 依赖项:

compile "io.ktor:ktor-client-okhttp:1.2.6"
compile "io.ktor:ktor-client-json:1.2.6"
compile "io.ktor:ktor-client-gson:1.2.6"

下面的请求对我来说很好。 Here is the relevant documentation(包括响应 JSON):

data class Vendors(val vendors: List<Vendor>)

data class Vendor(
    val id: String,
    val name: String,
    val roles: List<String>
)

fun fetchVendors() = runBlocking 
    httpClient.get<Vendors>("https://api.amazonalexa.com/v1/vendors") 
        headers.append("Authorization", "Bearer $accessToken")
    ?.vendors

更新:我知道一定有区别:上面的内容类型是application/json

但是这个调用(获取技能,使用documentation here)一直失败:

data class Skills(
    val isTruncated: Boolean,
    val skills: List<Skill>
)

data class Skill(
    val apis: List<String>,
    val asin: String,
    val lastUpdated: String,
    val nameByLocale: Map<String, String>,
    val publicationStatus: String,
    val skillId: String,
    val stage: String
)

fun fetchSkills(vendor: String) = runBlocking 
    httpClient.get<Skills>("https://api.amazonalexa.com/v1/skills?vendorId=$vendor") 
        headers.append("Authorization", "Bearer $accessToken")
    .skills

更新:这是响应内容类型:application/json+hal

我不明白为什么一个工作,而另一个失败,这个例外:

Exception in thread "main" io.ktor.client.call.NoTransformationFoundException: No transformation found: class kotlinx.coroutines.io.ByteBufferChannel -> class my.package.name.Skills
        at io.ktor.client.call.HttpClientCall.receive(HttpClientCall.kt:88)
        at my.package.name.AmazonApi$fetchSkills$1.invokeSuspend(AmazonApi.kt:200)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(PipelineContext.kt:215)
        at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:172)
        at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
        at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(PipelineContext.kt:122)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
        at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(PipelineContext.kt:215)
        at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:172)
        at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
        at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(PipelineContext.kt:122)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
        at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(PipelineContext.kt:215)
        at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:172)
        at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
        at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(PipelineContext.kt:122)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
        at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
        at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:270)
        at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:79)
        at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
        at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
        at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
        at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
        at my.package.name.AmazonApi.fetchSkills(AmazonApi.kt:131)
        at my.package.name.Cli.createProject(Cli.kt:305)
        at my.package.name.Cli.parseArgs(Cli.kt:208)
        at my.package.name.Cli$Companion.main(Cli.kt:508)
        at my.package.name.Cli.main(Cli.kt)

请给我一些提示如何解决这个问题,或者至少我可以如何调试它。顺便说一句,当我使用 String 而不是 Skill 时,我看到了预期的响应 JSON。

【问题讨论】:

我猜这是我在此期间发现的bug。仍然欢迎解决方法和提示。 【参考方案1】:

根据 GitHub 评论,我能够使用此解决方法:

val httpClient = HttpClient(OkHttp) 
    install(JsonFeature) 
        serializer = GsonSerializer()
        acceptContentTypes += ContentType("application", "json+hal")
    

但我仍在寻找更好的解决方案,而不会引起警告。

【讨论】:

【参考方案2】:

一般使用

implementation("io.ktor", "ktor-client-core", ktorVersion)
implementation("io.ktor", "ktor-client-serialization", ktorVersion)

然后像这样配置客户端:

HttpClient 
    install(JsonFeature) 
        serializer = KotlinxSerializer()
        // accept(ContentType.Application.Json)
    

将自动选择正确的引擎。当然,您可以使用其他序列化程序(例如 reikire 的答案中的 Gson)。

【讨论】:

以上是关于ktor 为某些数据类抛出 NoTransformationFoundException的主要内容,如果未能解决你的问题,请参考以下文章

ContentDisposition 类抛出不一致的异常

如何使用 Aspectj 捕获和抑制 Java 类抛出的异常

TailwindCSS 为暗模式类抛出错误

Ninject - 如何在构造过程中识别哪个类抛出异常

用户类抛出异常:org.apache.spark.sql.AnalysisException:无法推断 Parquet 的架构。必须手动指定

Apache Spark 2.4.0、AWS EMR、Spark Redshift 和 User 类抛出异常:java.lang.AbstractMethodError