一、现象
本地 astro dev 一切正常,但 npm run preview(即 wrangler dev)启动后,访问 /admin/login 页面,浏览器收到的是 白屏。更诡异的是:
- HTTP 状态码是
200 OK - 响应体只有 15 字节
- 内容显示为
[object Object]
而同一环境下的其他页面(如 /favicon.ico)能正常返回完整的 HTML。
二、排查过程
第一步:确认路由匹配
在 Worker entry 层加入了 debug 日志,看 app.render() 的入参:
[worker-entry] 🎯 app.render() 前, pathname: /admin/login routeData: true
路由是匹配到的(routeData: true),说明不是 404 问题。
第二步:追踪中间件
怀疑是中间件拦截后出了问题,在 callMiddleware 中打日志:
[callMiddleware] 📥 nextCalled: true value类型: object isResponse: true
[callMiddleware] ✅ 返回中间件的Response, status: 200
中间件正常返回了 Response 对象,不是这里的问题。
第三步:追踪渲染管道
Astro 的请求处理链是:
app.render()
→ AstroHandler.handle()
→ #actionsAndPages() // 检查 Actions
→ PagesHandler.handle() // 页面渲染
→ renderPage() // 实际渲染
逐层加日志后发现,renderPage() 确实被调用了:
[renderPage] 🎬 被调用!, isAstroComponent: true streaming: true
关键线索:streaming: true。
第四步:定位根因
Astro 默认启用流式渲染(streaming: true)。在标准 Node.js 环境中,renderPage() 返回的 Response 的 body 是一个 ReadableStream,浏览器可以正常消费。
但在 Cloudflare Workers 的 workerd 运行时中,这个 ReadableStream 对象在某个环节被错误地序列化——Response 对象被转成了字符串 "[object Object]",导致最终响应的 body 变成了这 15 个字符。
整个过程没有任何报错,日志安静地显示 status: 200,非常具有迷惑性。
三、解决方案
临时修复
在构建产物 dist/server/chunks/worker-entry_*.mjs 中,找到:
const app = createApp();
改为:
const app = createApp({ streaming: false });
重启 wrangler,页面恢复正常。
自动化修复
直接改构建产物不持久——每次 astro build 都会被覆盖。于是写了一个 post-build 脚本 scripts/patch-worker-entry.mjs,自动在每次构建后完成以下修补:
- 禁用 streaming:从
entry.mjs解析出 chunk 文件名(含 hash),替换createApp()为createApp({ streaming: false }) - 注入 Worker env:在 Worker 的
fetchhandler 层面将env绑定到globalThis.__env__,供后续中间件和业务代码读取
同时将脚本集成到 package.json 的 preview 和 deploy 命令中:
"preview": "npm run build && node scripts/patch-worker-entry.mjs && wrangler dev ...",
"deploy": "npm run build && node scripts/patch-worker-entry.mjs && ... && wrangler deploy"
四、延伸:环境变量安全
排查过程中还发现 wrangler.json 中硬编码了数据库密钥和管理密码。正确的做法是:
- 敏感变量(
TURSO_AUTH_TOKEN、SHUOSHUO_PASSWORD)→ 在 Cloudflare Pages Dashboard 的 Environment variables 中设置 .env文件已被.gitignore,仅用于本地开发wrangler.json中只保留空占位符,不提交真实值
五、总结
| 环节 | 关键发现 |
|---|---|
| 问题表象 | 200 OK + 15 字节 [object Object] |
| 错误方向 | 中间件、路由、认证逻辑全部正常 |
| 真正原因 | workerd 运行时不兼容 ReadableStream 响应体 |
| 最小修复 | createApp({ streaming: false }) |
| 长效方案 | post-build patch 脚本,自动禁用 streaming + 注入 env |
教训:在 Cloudflare Workers 上部署 Astro 时,如果遇到奇怪的「200 OK 但 body 是乱码/空/[object Object]」问题,先检查 streaming 是否被启用。这是 workerd 和 Node.js 运行时差异的典型表现。
💬 评论
加载中...