Svelte TypeScript中如何从服务端返回ReadableStream到客户端?
解决Next.js App Router中Server Action返回ReadableStream的序列化错误
错误原因
Next.js 13+ App Router的Server Action强制要求返回可序列化的POJO(Plain Old JavaScript Object),ReadableStream、Response这类对象不属于可序列化范畴,直接返回就会触发Cannot stringify arbitrary non-POJOs错误。Server Action的设计目标是传递状态数据,而非直接处理HTTP响应流。
可行解决方案
1. 改用API Route处理流式响应
将流式逻辑迁移到app/api/[route-name]/route.ts文件中,在GET/POST方法里直接返回new Response(stream):
// app/api/stream/route.ts export async function POST(request: Request) { // 构建ReadableStream的逻辑 const stream = new ReadableStream({ async start(controller) { // 写入流数据示例 controller.enqueue(new TextEncoder().encode("流式响应内容片段")); controller.close(); }, }); return new Response(stream, { headers: { "Content-Type": "text/plain; charset=utf-8" }, }); }
前端通过fetch调用该API Route,直接消费流式数据:
const response = await fetch("/api/stream", { method: "POST" }); const reader = response.body.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; console.log(new TextDecoder().decode(value)); }
2. 用Server Action返回触发信号,前端单独请求流
如果必须保留Server Action的调用流程,可让Action返回一个标识(比如会话ID),前端收到后再发起流式请求:
// Server Action export async function startStreamAction() { // 生成会话ID或执行前置业务逻辑 const sessionId = crypto.randomUUID(); return { sessionId }; }
前端调用Action后,再基于返回的标识请求API Route获取流:
const { sessionId } = await startStreamAction(); const response = await fetch(`/api/stream?sessionId=${sessionId}`); // 消费流数据逻辑...
3. 启用实验性流式Server Action(谨慎使用)
Next.js提供了实验性特性支持Server Action返回流,需在next.config.js中开启:
/** @type {import('next').NextConfig} */ const nextConfig = { experimental: { useServerActionStreaming: true, }, }; module.exports = nextConfig;
开启后可在Server Action中直接返回ReadableStream,但该特性仍在开发阶段,可能存在兼容性问题,不建议生产环境使用。
内容的提问来源于stack exchange,提问作者Flavien Mathieu
相关产品推荐
相关产品推荐

