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

Spring Reactive Rest Controller异常:无法返回application/json,提示无编码器

Spring WebFlux反应式控制器返回JSON响应时编码异常的解决方法

抛出异常:

org.springframework.http.converter.HttpMessageNotWritableException: No Encoder for [reactor.core.publisher.Mono<com.example.demo.Potato>] with preset Content-Type 'null'

技术栈

  • Kotlin 1.8.22
  • Java 17.0.7 LTS (Amazon Corretto 17.0.7.7.1 (build 17.0.7+7-LTS))
  • Spring Boot 3.1.2-SNAPSHOT
  • Spring Boot-starter-webflux 3.1.1
  • Jackson module kotlin 2.15.2
  • Jackson Core 2.15.2
  • Jackson Databind 2.15.2

示例代码

package com.example.demo

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Mono

@SpringBootApplication class DemoApplication
fun main(args: Array<String>) {
    runApplication<DemoApplication>(*args)
}

@RestController
class DemoController {
    @GetMapping(
            "demo-get",
            consumes = [MediaType.ALL_VALUE],
            produces = [MediaType.APPLICATION_JSON_VALUE],
    )
    suspend fun getDemo(): ResponseEntity<Mono<Potato>> =
            ResponseEntity.ok(Mono.just(Potato(test = "IamJustAtest")))
}

data class Potato(val test: String)

问题描述

在Spring Boot应用中创建反应式REST控制器,希望不使用.block()方法返回application/json类型响应,但请求/demo-get端点时抛出上述异常。

已尝试的方法

  • 使用.block()方法可以解决,但会降低性能且违背反应式理念。
  • 移除@GetMapping注解中的produces属性,响应Content-Type变为text/event-stream,标准HTTP客户端无法识别为JSON。
  • 将produces属性设置为application/json; charset=UTF-8,无效。
  • 在请求头中添加Accept: application/json,无效。

解决方法

问题出在控制器方法的返回类型上:返回ResponseEntity<Mono<Potato>>会让WebFlux尝试序列化Mono对象本身,而非订阅Mono获取内部的Potato数据。修改返回类型即可解决:

方式一:返回Mono<ResponseEntity<Potato>>

@RestController
class DemoController {
    @GetMapping(
            "demo-get",
            consumes = [MediaType.ALL_VALUE],
            produces = [MediaType.APPLICATION_JSON_VALUE],
    )
    suspend fun getDemo(): Mono<ResponseEntity<Potato>> =
            Mono.just(ResponseEntity.ok(Potato(test = "IamJustAtest")))
}

方式二:直接返回Mono<Potato>

WebFlux会自动生成200响应,无需手动包装ResponseEntity:

@RestController
class DemoController {
    @GetMapping(
            "demo-get",
            consumes = [MediaType.ALL_VALUE],
            produces = [MediaType.APPLICATION_JSON_VALUE],
    )
    suspend fun getDemo(): Mono<Potato> =
            Mono.just(Potato(test = "IamJustAtest"))
}

方式三:利用Kotlin suspend特性直接返回实体类

WebFlux会自动将suspend函数返回的实体包装为Mono:

@RestController
class DemoController {
    @GetMapping(
            "demo-get",
            consumes = [MediaType.ALL_VALUE],
            produces = [MediaType.APPLICATION_JSON_VALUE],
    )
    suspend fun getDemo(): Potato =
            Potato(test = "IamJustAtest")
}

修改后,WebFlux会正确识别响应类型,使用Jackson编码器将Potato序列化为JSON,并设置正确的Content-Type: application/json响应头。

内容的提问来源于stack exchange,提问作者Nick

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 17:10:34