如何在Next.js中启用Rewrite的文本流式响应?
Next.js Rewrite 无法传递流式响应的问题与解决方法
一、Next.js Rewrite 的核心差异
Next.js 默认的 Rewrite 配置是聚合式代理:它会先等待后端服务器完整返回整个响应内容,缓存后再转发给前端。这种行为破坏了流式响应的逐段输出特性,导致前端无法实时接收后端的流式数据——这就是直接调用后端端点正常,但用Rewrite转发就失效的原因。
二、实现流式响应转发的解决方案
要传递流式响应,不能用静态的 next.config.js Rewrite 配置,必须通过API Route 手动代理,因为API Route允许我们直接控制响应流的传递逻辑。
1. 创建代理API Route
根据你使用的Next.js路由模式,选择对应的实现方式:
(1)Pages Router 模式
在 pages/api/django/[...path].js 文件中编写代理逻辑:
export default async function handler(req, res) { const { path } = req.query; // 拼接后端目标地址 const targetUrl = `http://127.0.0.1:8000/${path.join('/')}`; // 转发请求到后端,开启流式支持 const backendResponse = await fetch(targetUrl, { method: req.method, headers: { ...req.headers, // 替换host头,避免后端的域名验证问题 host: '127.0.0.1:8000', }, body: req.body, // 禁用缓存,确保响应实时流式传递 cache: 'no-store', }); // 同步后端的响应状态码和头信息 res.status(backendResponse.status); for (const [key, value] of backendResponse.headers.entries()) { res.setHeader(key, value); } // 流式传递响应体到前端 const reader = backendResponse.body.getReader(); const stream = new ReadableStream({ async start(controller) { while (true) { const { done, value } = await reader.read(); if (done) break; controller.enqueue(value); } controller.close(); }, }); // 完成流的管道传输 await stream.pipeTo(res).catch(() => {}); }
(2)App Router 模式
在 app/api/django/[...path]/route.js 文件中编写代理逻辑:
// 处理GET请求 export async function GET(request, { params }) { const { path } = params; const targetUrl = `http://127.0.0.1:8000/${path.join('/')}`; const backendResponse = await fetch(targetUrl, { method: request.method, headers: { ...Object.fromEntries(request.headers), host: '127.0.0.1:8000', }, cache: 'no-store', }); // 直接返回后端的流式响应 return new Response(backendResponse.body, { status: backendResponse.status, headers: backendResponse.headers, }); } // 处理POST请求(如需支持其他方法,同理扩展) export async function POST(request, { params }) { const { path } = params; const targetUrl = `http://127.0.0.1:8000/${path.join('/')}`; const body = await request.text(); const backendResponse = await fetch(targetUrl, { method: 'POST', headers: { ...Object.fromEntries(request.headers), host: '127.0.0.1:8000', }, body, cache: 'no-store', }); return new Response(backendResponse.body, { status: backendResponse.status, headers: backendResponse.headers, }); }
2. 修改前端请求地址
把原来直接调用后端的请求地址,改成代理API的地址:
// 原代码:const response = await fetch(`http://127.0.0.1:8000/endpoint`, { ... }); // 修改后: const response = await fetch(`/api/django/endpoint`, { method: 'GET', headers: headers, }); // 原有的流式处理逻辑保持不变 let decoder = new TextDecoderStream(); if (!response.body) return; const reader = response.body .pipeThrough(decoder) .getReader(); while (true) { setLoading(true); var {value, done} = await reader.read(); if (done) { break; } else { tmpPromptResponse += value; if (currentchatId === chatId) { setMarkdownContent(tmpPromptResponse + "▌"); } } }
三、关键原理说明
API Route 绕过了Next.js Rewrite的默认缓存逻辑,直接将后端的响应流原封不动地转发给前端。通过设置 cache: 'no-store' 确保响应不会被中间层缓存,同时同步后端的响应头(如 Transfer-Encoding: chunked),保证前端能正确识别并处理流式数据。
内容的提问来源于stack exchange,提问作者Nathan Wang
相关产品推荐
相关产品推荐

