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

Spring Boot服务端转发FastAPI流式响应至前端遇阻,求简便实现方案

解决Spring Boot转发FastAPI流式text/event-stream响应的问题

问题分析

你之前的两种实现存在以下问题:

  • 返回Flux<String>的方式:若FastAPI返回的是原始字节流而非按行分割的字符串,bodyToFlux(String.class)可能会把整个流攒成一个字符串再返回,而非逐块转发;另外每次请求创建WebClient实例会造成资源浪费。
  • 返回ResponseEntity<Flux<ServerSentEvent<String>>>的方式:代码中返回类型声明与实际返回对象不匹配(RestResponse vs ResponseEntity),且如果FastAPI返回的不是标准SSE格式(即没有data:前缀和换行分隔符),bodyToFlux(ServerSentEvent.class)会解析失败,导致客户端收不到数据。

正确实现方案

方案1:原样转发原始字节流(推荐,完全无解析开销)

这种方式直接把FastAPI返回的字节流转发给客户端,不需要解析内容,确保100%原样传递,是最简便可靠的方式。

首先,注入全局的WebClient实例(不要每次请求创建):

@Configuration
public class WebClientConfig {
    @Bean
    public WebClient fastApiWebClient() {
        return WebClient.builder()
                .baseUrl("http://localhost:8000")
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .build();
    }
}

然后编写接口,返回Flux<DataBuffer>,并指定正确的响应头:

@RestController
public class StreamForwardController {

    private final WebClient fastApiWebClient;

    // 构造注入WebClient
    public StreamForwardController(WebClient fastApiWebClient) {
        this.fastApiWebClient = fastApiWebClient;
    }

    @PostMapping(value = "/streaming", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<DataBuffer> forwardStream(@RequestBody RequestDto request) {
        // 1. 完成身份校验逻辑(比如从request或header取token验证)
        validateAuth(request);

        // 2. 构造发送给FastAPI的请求体
        AiRequest aiRequest = buildAiRequest(request);

        // 3. 请求FastAPI并转发流式响应
        return fastApiWebClient.post()
                .uri("/aichat_dummy")
                .accept(MediaType.TEXT_EVENT_STREAM)
                .bodyValue(aiRequest)
                .retrieve()
                .bodyToFlux(DataBuffer.class); // 直接获取字节流,不做任何解析
    }

    // 身份校验逻辑示例
    private void validateAuth(RequestDto request) {
        // 实现你的鉴权逻辑,比如验证token、权限等
    }

    // 构造AI请求体示例
    private AiRequest buildAiRequest(RequestDto request) {
        // 根据客户端请求生成FastAPI需要的请求体
        return new AiRequest(request.getPrompt());
    }
}

方案2:处理标准SSE格式转发

如果FastAPI返回的是标准SSE格式(每条消息以data: xxx\n\n格式返回),可以用ServerSentEvent来解析并转发,确保Spring正确处理SSE的格式:

@PostMapping(value = "/streaming-sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> forwardSseStream(@RequestBody RequestDto request) {
    validateAuth(request);
    AiRequest aiRequest = buildAiRequest(request);

    ParameterizedTypeReference<ServerSentEvent<String>> sseType = new ParameterizedTypeReference<>() {};
    return fastApiWebClient.post()
            .uri("/aichat_dummy")
            .accept(MediaType.TEXT_EVENT_STREAM)
            .bodyValue(aiRequest)
            .retrieve()
            .bodyToFlux(sseType)
            // 原样转发SSE消息,也可以在这里修改消息内容
            .map(sse -> ServerSentEvent.builder(sse.data())
                    .id(sse.id())
                    .event(sse.event())
                    .retry(sse.retry())
                    .build());
}

关键注意事项

  1. 不要每次请求创建WebClient:应该通过@Bean注入全局实例,避免资源泄漏和性能损耗。
  2. 确保响应头正确:接口上的produces = MediaType.TEXT_EVENT_STREAM_VALUE必须指定,Spring会自动添加Transfer-Encoding: chunked头,不需要手动设置。
  3. 避免不必要的解析:如果不需要修改响应内容,直接转发DataBuffer是最优选择,不会引入解析开销,也不会因为格式不匹配导致失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 23:25:32