You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring WebFlux返回Kotlin数据类时为何使用CharSequenceEncoder而非KotlinSerializationJsonEncoder?

Spring WebFlux返回Kotlin数据类时为何使用CharSequenceEncoder而非KotlinSerializationJsonEncoder?

兄弟,我懂你遇到的这个坑——明明配置了Kotlinx Serialization当JSON序列化器,结果Spring WebFlux偏偏用CharSequenceEncoder来处理响应,日志里的输出看得人一脸懵。不过别担心,咱们一步步捋清楚问题出在哪,然后解决它!

先确认你的现有配置细节

先把你提供的代码按格式整理出来,方便咱们一起排查:

build.gradle.kts

plugins {
    alias(libs.plugins.kotlin.jvm)
    alias(libs.plugins.kotlin.serialization)
    alias(libs.plugins.kotlin.spring)
    alias(libs.plugins.spring.boot)
    alias(libs.plugins.spring.dependency.management)
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-webflux:3.5.3") {
        exclude(group = "com.fasterxml.jackson.core", module = "jackson-databind")
        exclude(group = "com.fasterxml.jackson.module", module = "jackson-module-kotlin")
    }
    implementation(libs.kotlinX.serialization)
}

libs.versions.toml

[versions]
spring = "3.5.3"
kotlin = "2.2.0"
serialization = "1.9.0"

kotlinX-serialization = {module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }

[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-spring = { id = "org.jetbrains.kotlin.plugin.spring", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
spring-boot = { id = "org.springframework.boot", version.ref = "spring" }
spring-dependency-management = { id = "io.spring.dependency-management", version = "1.1.7" }

控制器代码

@GetMapping("/trainSch", produces = [MediaType.APPLICATION_JSON_VALUE])
suspend fun getTrainSch(
    @RequestParam("trainNo") trainNumber: String
): ResponseEntity<TrainDetails> {
    return ResponseEntity.ok(
        TrainDetails(
            no = trainNumber,
            na = "Superfast Express",
            typ = 1,
            zn = 5,
            dly = 10,
            rake = "LHB",
            classes = 3,
            upd = "10:45 AM"
        )
    )
}

数据类

@Serializable
data class TrainDetails(
    val no: String,
    val na: String,
    val typ: Int,
    val zn: Int,
    val dly: Int,
    val rake: String,
    val classes: Int,
    val upd: String
)

你的WebFlux配置Bean

@Bean
fun webFluxConfigurer(
    encoder: KotlinSerializationJsonEncoder,
    decoder: KotlinSerializationJsonDecoder
): WebFluxConfigurer = object : WebFluxConfigurer {
    override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
        configurer.defaultCodecs().kotlinSerializationJsonEncoder(encoder)
        configurer.defaultCodecs().kotlinSerializationJsonDecoder(decoder)
    }
}

问题根源分析

从日志和配置来看,核心问题是:Spring WebFlux没有优先使用KotlinSerializationJsonEncoder序列化你的TrainDetails对象,而是先把对象转成了JSON字符串,再用CharSequenceEncoder输出。

可能的原因有这几个:

  1. 手动配置的WebFluxConfigurer和Spring Boot的自动配置冲突,导致Kotlinx Serialization的编码器没有被正确设置为首选
  2. @EnableWebFlux注解关闭了Spring Boot的自动配置,包括Codec的自动注册
  3. Codec链的优先级问题,CharSequenceEncoder的优先级高于KotlinSerializationJsonEncoder

具体解决方法

方法1:先试试移除手动配置,依赖自动配置

Spring Boot 3.x+ 已经内置了Kotlinx Serialization的自动支持——当你排除Jackson依赖且引入kotlinx-serialization-json时,KotlinSerializationWebFluxAutoConfiguration会自动注册对应的编码器和解码器。

操作步骤:

  1. 删掉你自己定义的webFluxConfigurer Bean
  2. 重启服务,调用接口查看日志

如果一切正常,日志里应该会显示KotlinSerializationJsonEncoder: Writing {...}而不是CharSequenceEncoder。

方法2:手动配置Codec(自动配置不生效时用)

如果自动配置没起作用,你可以手动调整Codec的注册方式,确保KotlinSerializationJsonEncoder被优先使用:

import kotlinx.serialization.json.Json
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.codec.CodecCustomizer
import org.springframework.http.codec.json.KotlinSerializationJsonDecoder
import org.springframework.http.codec.json.KotlinSerializationJsonEncoder

@Configuration
class WebFluxCodecConfig {

    @Bean
    fun kotlinSerializationCodecCustomizer(): CodecCustomizer {
        // 自定义Kotlinx Serialization的Json配置
        val jsonConfig = Json {
            ignoreUnknownKeys = true // 忽略未知字段,增强兼容性
            prettyPrint = false // 生产环境建议关闭格式化
        }

        val jsonEncoder = KotlinSerializationJsonEncoder(jsonConfig)
        val jsonDecoder = KotlinSerializationJsonDecoder(jsonConfig)

        return CodecCustomizer { configurer ->
            // 注册自定义编码器到Codec链,确保优先级
            configurer.customCodecs().register(jsonEncoder)
            configurer.customCodecs().register(jsonDecoder)
            
            // 替换默认的Kotlin Serialization Codec
            val defaultCodecs = configurer.defaultCodecs()
            defaultCodecs.kotlinSerializationJsonEncoder(jsonEncoder)
            defaultCodecs.kotlinSerializationJsonDecoder(jsonDecoder)
        }
    }
}

方法3:检查是否启用了@EnableWebFlux

如果你在配置类或启动类上添加了@EnableWebFlux注解,这会完全关闭Spring Boot的WebFlux自动配置,包括Codec的自动注册。

解决:如果没有特殊的全局WebFlux配置需求,建议移除@EnableWebFlux注解,让Spring Boot的自动配置帮你处理Codec的注册。

验证结果

修改完成后,重启服务调用/trainSch?trainNo=12345接口:

  1. 查看TRACE级别日志,应该会看到KotlinSerializationJsonEncoder: Writing {...}的输出
  2. Postman里的响应内容依然保持正确的JSON格式

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 12:49:29