@microsoft fetchEventSource 无法流式返回多语言翻译响应问题
问题描述
后端实现多语言翻译的流式响应,Postman测试时单个翻译完成后会立即返回结果,但在React项目中使用@microsoft/fetchEventSource发送POST请求时,需等所有翻译完成才会收到响应。
错误分析
从提供的代码中可定位出以下核心问题:
- FormData缺失核心参数:
SubmitHandler接收了input(待翻译文本)参数,但未将其添加到FormData中,后端无法获取待翻译内容,直接导致请求逻辑异常。 - useEffect依赖项错误:当前useEffect依赖
requestData?.signal,但requestData是包含FormData和signal的对象,每次调用SubmitHandler都会生成新的requestData对象,依赖signal无法触发useEffect重新执行,导致请求未正确发起或复用旧上下文。 - 缺少SSE必要请求头:未声明
Accept: text/event-stream,部分浏览器或代理会将请求当作普通HTTP请求处理,缓存整个响应直到完成后才返回。 - fetchEventSource配置不全:POST请求的SSE需要开启
keepalive: true维持连接,否则可能被浏览器主动断开,导致流式响应失效。
解决方案
修改useMultiTranslate Hook代码如下(注释标注关键修改点):
import { useRef, useState, useEffect } from "react"; import { useStore } from "@/store"; import { fetchEventSource } from '@microsoft/fetch-event-source'; export const useMultiTranslate = () => { const abortController = useRef(null); const src_lang = useStore((state) => state.srcLanguage); const [requestData, setRequestData] = useState(null); // 修正:正确解构store的状态更新函数(原解构方式会导致状态更新异常) const setTranslations = useStore((state) => state.setTranslations); const url = 'api/v2/infere/multi'; useEffect(() => { if (!requestData) return; const { request, signal } = requestData; // 构建包含SSE必要字段的请求头 const headers = {}; if (sessionStorage.getItem("token")) { headers["Authorization"] = `Bearer ${sessionStorage.getItem("token")}`; } // 明确声明接受SSE流式响应 headers["Accept"] = "text/event-stream"; // 禁止缓存响应 headers["Cache-Control"] = "no-cache"; const streamTranslations = async () => { try { await fetchEventSource(url, { method: "POST", body: request, headers: headers, signal: signal, // 开启keepalive维持POST请求的SSE连接 keepalive: true, // 允许页面隐藏时仍保持连接(可选,根据需求调整) openWhenHidden: true, onmessage(ev) { console.log(`Received event: ${ev.data}`); // 增加JSON解析容错 try { const newTranslation = JSON.parse(ev.data); setTranslations((prev) => ({ ...prev, ...newTranslation })); } catch (parseErr) { console.error("Failed to parse event data:", parseErr); } }, onerror(err) { console.error("Streaming error: ", err); // 抛出错误终止请求,避免无限重连 throw err; }, onclose() { console.log("Streaming connection closed"); } }); } catch (error) { // 排除主动中止请求的错误 if (!signal.aborted) { console.error('Failed to stream response:', error); } } }; streamTranslations(); // 清理函数:终止旧请求,避免内存泄漏 return () => { abortController.current?.abort(); }; }, [requestData]); // 修正:依赖整个requestData对象,确保每次请求变化都触发 const SubmitHandler = (input, sId) => { // 终止之前的请求(如果存在) abortController.current?.abort(); abortController.current = new AbortController(); const request = new FormData(); // 修正:添加待翻译文本到FormData request.append("text", input); request.append("src_lang", src_lang); request.append("mt_dest_lang", "multi"); // 按需添加sId参数 if (sId) request.append("sId", sId); const signal = abortController.current.signal; setRequestData({ request, signal }); }; return SubmitHandler; };
同时修正TextboxToolbar中的调用逻辑:
const multiTranslate = () => { if (inputText.trim().length) { SubmitHandler(inputText.trim(), sId); } };
额外检查点
- 确认后端返回的SSE格式符合标准:每条消息以
data:开头,结尾用\n\n分隔,示例:data: {"lang": "zh", "translation": "你好"} data: {"lang": "en", "translation": "Hello"} - 检查前端是否有代理或拦截器(如axios拦截器)修改了请求头或响应处理,导致SSE被转换为普通响应。
- 查看浏览器控制台是否存在CORS或其他网络错误,这类错误可能会中断流式响应。
内容的提问来源于stack exchange,提问作者mostafa galal
相关产品推荐
相关产品推荐

