Spring Boot WebFlux如何移除流式输出中的data:前缀?
Spring WebFlux流式发送JSON移除SSE data前缀方案
一、data:前缀是不是必须的?
- 要是用标准SSE的
EventSourceAPI接收,那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
相关产品推荐
相关产品推荐

