让ktor的ApplicationCall.receive()支持x-www-form-urlencoded

kuku 发布于 21 天前 5 次阅读


按照ktor的文档中配置ContentNegotiation,使用call.receive()默认只支持application/json,可以编写自定义的转换器并注册以支持其它的Content-Type

1、编写转换器

class FormUrlEncodedConverter(private val objectMapper: ObjectMapper) : ContentConverter {

    override suspend fun serialize(
        contentType: ContentType,
        charset: Charset,
        typeInfo: TypeInfo,
        value: Any?
    ): OutgoingContent {
        return OutputStreamContent(
            {
                val jsonNode = objectMapper.readTree(objectMapper.writeValueAsString(value))
                val sb = StringBuilder()
                jsonNode.fieldNames().forEach {
                    sb.append(it).append(jsonNode.get(it)).append("&")
                }
                objectMapper.writeValue(this, sb.removeSuffix("&").toString())
            },
            contentType.withCharsetIfNeeded(charset)
        )
    }

    override suspend fun deserialize(charset: Charset, typeInfo: TypeInfo, content: ByteReadChannel): Any? {
        try {
            return withContext(Dispatchers.IO) {
                val reader = content.toInputStream().reader(charset)
                val body = reader.readText()
                val objectNode = objectMapper.createObjectNode()
                body.split("&").forEach {
                    val arr = it.split("=")
                    val k = arr[0]
                    val v = URLDecoder.decode(arr[1], "utf-8")
                    objectNode.put(k, v)
                }
                objectMapper.treeToValue(objectNode, objectMapper.constructType(typeInfo.reifiedType))
            }
        } catch (deserializeFailure: Exception) {
            val convertException = JsonConvertException("Illegal json parameter found", deserializeFailure)

            when (deserializeFailure) {
                is JsonParseException -> throw convertException
                is JsonMappingException -> throw convertException
                else -> throw deserializeFailure
            }
        }
    }
}

2、注册

install(ContentNegotiation) {
        val mapper = ObjectMapper()
        mapper.apply {
                setDefaultPrettyPrinter(
                        DefaultPrettyPrinter().apply {
                                indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance)
                                indentObjectsWith(DefaultIndenter("  ", "\n"))
                        }
                )
        }
        mapper.registerKotlinModule()
        register(ContentType.Application.Json, JacksonConverter(mapper, true))
        register(ContentType.Application.FormUrlEncoded, FormUrlEncodedConverter(mapper))
}
此作者没有提供个人介绍。
最后更新于 2025-09-24