2
0
Просмотр исходного кода

♻️ refactor(server): 重构 Hono 中间件实现方式

- 修改中间件定义方式,移除 next 参数以符合 Hono 最佳实践
- 修复 Vite 中间件和 sirv 中间件的响应处理逻辑,确保正确返回 c.body
- 重构 SSR 渲染流程,使用 Readable 流处理 HTML 内容生成
- 实现基于 AbortController 的超时中止机制,增强错误处理能力
- 优化响应头设置和状态码处理逻辑
- 修复 Vite 开发服务器集成问题,确保开发环境正常工作

🐛 fix(server): 修复 SSR 渲染响应处理问题

- 修复使用 c.env 获取原生请求响应对象的关键问题
- 修复多个返回点未正确返回响应的问题
- 解决流处理过程中可能的内存泄漏问题
- 修复未正确清理超时定时器的问题
yourname 7 месяцев назад
Родитель
Сommit
80e9b488e9
1 измененных файлов с 38 добавлено и 24 удалено
  1. 38 24
      server.js

+ 38 - 24
server.js

@@ -1,6 +1,8 @@
 import fs from 'node:fs/promises';
 import { URL } from 'node:url';
 import { Transform } from 'node:stream';
+import { Readable } from 'node:stream';
+import { pipeline } from 'node:stream/promises';
 import { serve } from '@hono/node-server';
 import { Hono } from 'hono';
 import { createServer as createNodeServer } from 'node:http';
@@ -46,8 +48,8 @@ if (!isProduction) {
   });
 }
 
-// 将原有请求处理逻辑转换为 Hono 中间件
-app.use(async (c, next) => {
+// 将请求处理逻辑适配为 Hono 中间件
+app.use(async (c) => {
   try {
     // 使用 c.env 获取原生请求响应对象(关键修复)
     const req = c.env.incoming;
@@ -72,7 +74,7 @@ app.use(async (c, next) => {
       
       // 如果 Vite 中间件已经处理了请求,直接返回
       if (handled) {
-        return;
+        return c.body;
       }
     } 
     // 生产环境:使用 compression 和 sirv 中间件
@@ -83,7 +85,7 @@ app.use(async (c, next) => {
       });
       
       if (compressed) {
-        return;
+        return c.body;
       }
       
       // 再尝试 sirv 中间件处理静态文件
@@ -92,7 +94,7 @@ app.use(async (c, next) => {
       });
       
       if (served) {
-        return;
+        return c.body;
       }
     }
 
@@ -114,31 +116,42 @@ app.use(async (c, next) => {
     }
 
     let didError = false;
+    let abortController;
 
+    // 创建一个可读流用于 SSR 渲染内容
+    const [htmlStart, htmlEnd] = template.split(`<!--app-html-->`);
+    const ssrStream = new Readable({ read: () => {} });
+    
+    // 写入 HTML 头部
+    ssrStream.push(htmlStart);
+
+    // 设置响应头和状态码
+    c.header('Content-Type', 'text/html');
+    
+    // 处理渲染
     const { pipe, abort } = render(normalizedUrl, {
       onShellError() {
-        res.writeHead(500, { 'Content-Type': 'text/html' });
-        res.end('<h1>Something went wrong</h1>');
+        didError = true;
+        c.status(500);
+        ssrStream.push('<h1>Something went wrong</h1>');
+        ssrStream.push(null); // 结束流
       },
       onShellReady() {
-        res.writeHead(didError ? 500 : 200, { 'Content-Type': 'text/html' });
-
+        // 将渲染结果通过管道传入 ssrStream
         const transformStream = new Transform({
           transform(chunk, encoding, callback) {
-            res.write(chunk, encoding);
+            ssrStream.push(chunk, encoding);
             callback();
-          },
+          }
         });
 
-        const [htmlStart, htmlEnd] = template.split(`<!--app-html-->`);
-
-        res.write(htmlStart);
-
+        pipe(transformStream);
+        
+        // 当 transformStream 完成时,添加 HTML 尾部
         transformStream.on('finish', () => {
-          res.end(htmlEnd);
+          ssrStream.push(htmlEnd);
+          ssrStream.push(null); // 结束流
         });
-
-        pipe(transformStream);
       },
       onError(error) {
         didError = true;
@@ -147,17 +160,19 @@ app.use(async (c, next) => {
     });
 
     // 设置超时中止
+    abortController = new AbortController();
     const abortTimeout = setTimeout(() => {
       abort();
+      abortController.abort();
     }, ABORT_DELAY);
 
-    // 清理超时
-    res.on('finish', () => {
-      clearTimeout(abortTimeout);
+    // 将流通过 Hono 响应返回
+    return c.body(ssrStream, {
+      onEnd: () => {
+        clearTimeout(abortTimeout);
+      }
     });
 
-    // 告诉 Hono 我们已经手动处理了响应
-    c.res = new Response(null, { status: 200 });
   } catch (e) {
     if (!isProduction && vite) {
       vite.ssrFixStacktrace(e);
@@ -175,4 +190,3 @@ serve({
 }, () => {
   console.log(`Server started at http://localhost:${port}`);
 });
-