首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ktor客户端post请求导致错误` `lateinit属性nextElementName尚未初始化‘

ktor客户端post请求导致错误` `lateinit属性nextElementName尚未初始化‘
EN

Stack Overflow用户
提问于 2022-05-18 09:06:14
回答 2查看 320关注 0票数 2

我试图使用ktor向远程API发出简单的post请求。只有一个端点,请求和响应主体在JSON中。

我在用

  • ktor 2.0.1
  • 科特林1.6.21
  • Kotlinx系列化1.3.2
  • Kotlin合作社1.6.1

我需要发布的端点是https://mods.factorio.com/api/v2/mods/releases/init_upload - 这里的细节

我创造了一个客户

代码语言:javascript
复制
private val client = HttpClient(CIO) {
  install(Resources)
  install(Logging) {
    logger = Logger.DEFAULT
    level = LogLevel.HEADERS
  }
  install(ContentNegotiation) {
    json(Json {
      prettyPrint = true
      isLenient = true
    })
  }
  defaultRequest {
    header(HttpHeaders.Authorization, "Bearer 123")
  }
  followRedirects = false
  expectSuccess = true
}

我用它来请求发邮件

代码语言:javascript
复制
  val response = client.post(portalUploadEndpoint) {
    contentType(ContentType.Application.Json)
    setBody(InitUploadRequest("123"))
  }

请求和响应体是JSON对象,我使用的是Kotlinx序列化。(请参阅问题底部的完整代码。)

不管怎么说我都犯了错误。我做错了什么吗?

代码语言:javascript
复制
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property nextElementName has not been initialized
    at io.ktor.resources.serialization.ParametersEncoder.encodeValue(ParametersEncoder.kt:26)
    at kotlinx.serialization.encoding.AbstractEncoder.encodeString(AbstractEncoder.kt:51)
    at kotlinx.serialization.internal.StringSerializer.serialize(Primitives.kt:141)
    at kotlinx.serialization.internal.StringSerializer.serialize(Primitives.kt:138)
    at kotlinx.serialization.encoding.Encoder$DefaultImpls.encodeSerializableValue(Encoding.kt:282)
    at kotlinx.serialization.encoding.AbstractEncoder.encodeSerializableValue(AbstractEncoder.kt:18)
    at io.ktor.resources.serialization.ResourcesFormat.encodeToParameters(ResourcesFormat.kt:90)
    at io.ktor.resources.UrlBuilderKt.href(UrlBuilder.kt:49)
    at MainKt$main$1.invokeSuspend(main.kt:163)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
    at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    at MainKt.main(main.kt:47)
    at MainKt.main(main.kt)

全码

代码语言:javascript
复制
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.logging.DEFAULT
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.plugins.resources.Resources
import io.ktor.client.plugins.resources.post
import io.ktor.client.request.header
import io.ktor.client.request.setBody
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.URLBuilder
import io.ktor.http.contentType
import io.ktor.http.takeFrom
import io.ktor.resources.Resource
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.jsonObject


private val client = HttpClient(CIO) {
  install(Resources)
  install(Logging) {
    logger = Logger.DEFAULT
    level = LogLevel.HEADERS
  }
  install(ContentNegotiation) {
    json(Json {
      prettyPrint = true
      isLenient = true
    })
  }
  defaultRequest {
    header(HttpHeaders.Authorization, "Bearer 123")
  }
  followRedirects = false
  expectSuccess = true
}

fun main() = runBlocking {

  val apiBase = "https://mods.factorio.com/api/v2"
  val uploadEndpoint = "mods/releases/"

  val portalUploadEndpoint = URLBuilder(apiBase).apply {
    takeFrom(uploadEndpoint)
  }.buildString()

  println(portalUploadEndpoint)

  val response = client.post(portalUploadEndpoint) {
    contentType(ContentType.Application.Json)
    setBody(InitUploadRequest("123"))
  }

  println(response)
}

@Resource("/init_upload")
@Serializable
data class InitUploadRequest(
  @SerialName("mod") val modName: String,
)


@Serializable(with = InitUploadResponse.Serializer::class)
sealed interface InitUploadResponse {

  /**
   * @param[uploadUrl] URL the mod zip file should be uploaded to
   */
  @Serializable
  data class Success(
    @SerialName("upload_url") val uploadUrl: String,
  ) : InitUploadResponse

  object Serializer :
    JsonContentPolymorphicSerializer<InitUploadResponse>(InitUploadResponse::class) {
    override fun selectDeserializer(element: JsonElement) = when {
      "upload_url" in element.jsonObject -> Success.serializer()
      else                               -> Failure.serializer()
    }
  }
}

@Serializable
data class Failure(
  val error: String? = null,
  val message: String? = null,
) : InitUploadResponse

另请参阅

我也在YouTrack https://youtrack.jetbrains.com/issue/KTOR-4342/上报道过这件事

EN

Stack Overflow用户

发布于 2022-05-18 15:32:43

问题是,您要将一个String (URL)传递给HttpClient.post扩展函数,该扩展函数需要一个Resource。您需要在那里传递一个资源,或者使用来自io.ktor.client.request包的HttpClient.post扩展函数。

票数 2
EN
查看全部 2 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72286223

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档