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

Kotlin反应式编程问题:移除.block()改用流式处理后,组合DTO无法正确序列化返回

问题分析与解决方案

我来帮你排查这个问题——你遇到的核心问题是Reactive类型(Mono)的序列化异常:当你把Mono<MutableList<CustomObjectDto>>直接放进响应DTO时,Spring并没有等待异步操作完成、获取实际数据后再序列化,而是直接序列化了Mono对象本身的内部状态,这就导致返回了scanAvailable: true这种非预期结果。

问题根源拆解

  1. 响应DTO的字段类型错误:你的CustomObjectResultDto中results字段定义为Mono<MutableList<CustomObjectDto>>,但Spring默认不会自动订阅Reactive类型并提取内部数据,只会序列化对象的元数据(比如scanAvailable)。
  2. Service层的返回值设计不符合Reactive范式:当前Service直接返回CustomObjectResultDto,但里面的count和result都是未完成的异步操作(Mono),没有合并异步结果就直接封装成DTO返回了。
  3. Controller层没有正确处理Reactive响应:Controller返回的是包含未处理Mono的HttpResponse,框架无法识别需要等待异步操作完成。

修复步骤与代码调整

1. 修正响应DTO的字段类型

去掉results字段的Mono包裹,直接用实际数据类型:

data class CustomObjectResultDto(
  val resultsLength: Long?,
  @JsonInclude val results: MutableList<CustomObjectDto>
)

2. 重构Service层,返回合并后的异步结果

使用Mono.zip合并count和查询结果两个异步操作,确保两者都完成后再构建最终DTO:

override fun listing(
  ctx: context,
  filterConfig: FilterConfig,
  filterParams: FilterParams
): Mono<CustomObjectResultDto> {
  // 获取查询结果的Mono
  val resultMono = repository.listing(ctx, filterParams, filterConfig)
    .flatMapIterable { list: List<CustomObject> -> list.map { it.toItemDto() } }
    .collectList()
  
  // 获取计数的Mono(确保repository.count返回的是Mono<Long>,如果是阻塞方法请改为Reactive版本)
  val countMono = repository.count(ctx, filterParams)

  // 合并两个异步操作,构建最终DTO
  return Mono.zip(countMono, resultMono)
    .map { (count, results) -> CustomObjectResultDto(count, results) }
}

3. 调整Controller层,返回Reactive响应

让Controller返回Mono<MutableHttpResponse<CustomObjectResultDto>>,或者直接返回Mono<CustomObjectResultDto>(Spring WebFlux会自动处理订阅和序列化):

// 示例:返回Mono包裹的HttpResponse
fun listing(
  // 你的请求参数:partnerId, userId, correlationId等
): Mono<MutableHttpResponse<CustomObjectResultDto>> {
  val ctx = context(partnerId, userId, correlationId)
  // ...日志逻辑...
  
  val filterParams = FilterParams(userName, applicationType, status, wifiStatus, cardNumber)
  val filterConfig = FilterConfig(pageLimit, pageIndex, PAGE_ORDER_BY_VALUES.getValue(orderBy), order)

  // 订阅Service返回的Mono,构建响应
  return service.listing(ctx, filterConfig, filterParams)
    .map { resultDto -> HttpResponse.ok(resultDto) }
}

如果使用注解式Controller(比如@GetMapping),可以更简洁:

@GetMapping("/custom-objects")
fun listing(
  // 请求参数
): Mono<CustomObjectResultDto> {
  // ...上下文和参数构建逻辑...
  return service.listing(ctx, filterConfig, filterParams)
}

为什么这样改能解决问题?

  • 响应DTO不再包含Reactive类型,序列化时直接处理实际的实体列表,符合你预期的JSON格式。
  • Mono.zip确保了count和查询结果两个异步操作都完成后才构建DTO,避免了异步操作未完成就返回的问题。
  • Controller返回Mono类型,Spring WebFlux会自动订阅这个异步流,等待数据就绪后再序列化返回给客户端。

内容的提问来源于stack exchange,提问作者Kristóf Nagy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 06:54:32