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

Spring Boot WebFlux如何移除流式输出中的data:前缀?

Spring WebFlux流式发送JSON移除SSE data前缀方案

一、data:前缀是不是必须的?

  • 要是用标准SSE的EventSource API接收,那data:前缀是必填的——这是SSE协议规定的格式,客户端依赖这个前缀来解析消息。但你用fetch()直接读流的话,就不受SSE协议约束了,这个前缀完全没必要,只是Spring WebFlux默认返回TEXT_EVENT_STREAM_VALUE类型时,会自动按SSE格式包装消息才加上的。

二、移除前缀同时保留流式功能的两种方法

方法1:自定义编码器绕过SSE包装

Spring WebFlux返回TEXT_EVENT_STREAM_VALUE时,会用内置的SseEncoder给每条消息加data:前缀。我们可以删掉这个编码器,让框架直接输出原始字符串:

先写个配置类:

import org.springframework.context.annotation.Configuration;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.config.WebFluxConfigurer;

@Configuration
public class WebFluxStreamConfig implements WebFluxConfigurer {
    @Override
    public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
        // 移除默认的SSE编码器,避免消息被包装
        configurer.getWriters().removeIf(writer -> writer.getClass().getSimpleName().equals("SseEncoder"));
    }
}

然后控制器里不要用TEXT_EVENT_STREAM_VALUE,换成普通的application/json或者text/plain,返回序列化好的JSON字符串流:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.time.Duration;
import java.util.List;

@RestController
public class StreamController {
    private final ObjectMapper objectMapper = new ObjectMapper();

    @GetMapping(value = "/stream/json", produces = MediaType.APPLICATION_JSON_VALUE)
    public Flux<String> streamJson() {
        List<MyObject> dataList = List.of(new MyObject("张三", 25), new MyObject("李四", 30));
        return Flux.fromIterable(dataList)
                .map(obj -> {
                    try {
                        // 把对象序列化成JSON字符串
                        return objectMapper.writeValueAsString(obj);
                    } catch (Exception e) {
                        throw new RuntimeException("序列化失败", e);
                    }
                })
                .delayElements(Duration.ofSeconds(1)); // 模拟间隔发送,体现流式效果
    }

    // 测试用的实体类
    static class MyObject {
        private String name;
        private int age;

        public MyObject(String name, int age) {
            this.name = name;
            this.age = age;
        }

        // getter/setter 按需添加
        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }
}

方法2:直接返回对象流,用application/stream+json类型

更简单的方式:不用手动转字符串,直接返回Flux<MyObject>,指定媒体类型为application/stream+json——Spring WebFlux会自动把每个对象流式序列化成JSON,而且不会加任何SSE前缀:

@GetMapping(value = "/stream/objects", produces = "application/stream+json")
public Flux<MyObject> streamObjects() {
    List<MyObject> dataList = List.of(new MyObject("张三", 25), new MyObject("李四", 30));
    return Flux.fromIterable(dataList)
            .delayElements(Duration.ofSeconds(1));
}

这种方式更符合HTTP流式JSON的规范,客户端用fetch()接收时,每次会拿到完整的单个JSON对象,直接处理就行。

额外提醒

  • 用application/stream+json时,客户端要注意处理分块流:每个JSON对象是单独的块,需要逐行读取(因为流式JSON不会把所有对象包在一个数组里)。
  • 别同时用TEXT_EVENT_STREAM_VALUE和自定义编码器,不然SSE的包装逻辑还是会跑出来。

内容的提问来源于stack exchange,提问作者Bằng Rikimaru

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 05:44:52