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
相关产品推荐
相关产品推荐

