For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt, and this page is available as Markdown at /zh/guides/advanced-features/web-server.md.
  • 简体中文
  • 自定义 Web Server

    Modern.js 将大部分项目需要的服务端能力都进行了封装,通常项目无需进行服务端开发。但在有些开发场景下,例如用户鉴权、请求预处理、添加页面渲染骨架等,项目仍需要对服务端进行定制。

    要在 Modern.js 项目中使用自定义 Web Server,请按照以下步骤操作:

    1. 安装 @modern-js/server-runtime 依赖

    如果项目尚未安装 @modern-js/server-runtime 依赖,请先安装:

    pnpm add @modern-js/server-runtime
    版本一致性

    请确保 @modern-js/server-runtime 的版本与项目中 @modern-js/app-tools 的版本保持一致。Modern.js 的所有官方包使用统一版本号发布,版本不一致可能导致兼容性问题。

    请先查看 @modern-js/app-tools 的版本,然后安装相同版本的 @modern-js/server-runtime:

    # 查看当前 @modern-js/app-tools 的版本
    pnpm list @modern-js/app-tools
    
    # 安装相同版本的 @modern-js/server-runtime
    pnpm add @modern-js/server-runtime@<版本号>
    1. 创建 server 目录和配置文件

    在项目根目录下创建 server/modern.server.ts 文件:

    server/modern.server.ts
    import { defineServerConfig } from '@modern-js/server-runtime';
    
    export default defineServerConfig({
      middlewares: [], // 中间件
      renderMiddlewares: [], // 渲染中间件
      plugins: [], // 插件
      onError: () => {}, // 错误处理
    });

    创建文件后,可以在这个文件中编写自定义逻辑。

    1. 你可以将 server 目录包含在 tsconfig.json 中
    {
      // ...
      "include": [
        // ...
        "server",
      ],
    }

    自定义 Web Server 能力

    Modern.js 的服务器基于 Hono 实现,在最新版本的自定义 Web Server 中,我们向用户暴露了 Hono 的中间件能力,你可以参考 Hono 文档 了解更多用法。

    server/modern.server.ts 文件中添加如下配置来扩展 Server:

    • 中间件(Middleware)
    • 渲染中间件(RenderMiddleware)
    • 服务端插件(Plugin)

    其中 Plugin 中可以定义 Middleware 与 RenderMiddleware。 中间件加载流程如下图所示:

    基本配置

    server/modern.server.ts
    import { defineServerConfig } from '@modern-js/server-runtime';
    
    export default defineServerConfig({
      middlewares: [], // 中间件
      renderMiddlewares: [], // 渲染中间件
      plugins: [], // 插件
      onError: () => {}, // 错误处理
    });

    类型定义

    defineServerConfig 类型定义如下:

    import type { MiddlewareHandler } from 'hono';
    
    type MiddlewareObj = {
      name: string;
      path?: string;
      method?: 'options' | 'get' | 'post' | 'put' | 'delete' | 'patch' | 'all';
      handler: MiddlewareHandler | MiddlewareHandler[];
    };
    type ServerConfig = {
      middlewares?: MiddlewareObj[];
      renderMiddlewares?: MiddlewareObj[];
      plugins?: ServerPlugin[];
      onError?: (err: Error, c: Context) => Promise<any> | any;
    };

    Middleware

    Middleware 支持在 Modern.js 服务的请求处理与页面路由的流程前后,执行自定义逻辑。 即自定义逻辑既要处理接口路由,也要作用于页面路由,那么 Middleware 是不二选择。

    Note

    如果仅需要处理 BFF 接口路由,可以通过检查 req.path 是否以 BFF prefix 开头,来判断是否为 BFF 接口请求。

    使用姿势如下:

    server/modern.server.ts
    import {
      defineServerConfig,
      type MiddlewareHandler,
    } from '@modern-js/server-runtime';
    
    export const handler: MiddlewareHandler = async (c, next) => {
      const monitors = c.get('monitors');
      const start = Date.now();
    
      await next();
    
      const end = Date.now();
      // 上报耗时
      monitors.timing('request_timing', end - start);
    };
    
    export default defineServerConfig({
      middlewares: [
        {
          name: 'request-timing',
          handler,
        },
      ],
    });
    Warning

    必须执行 next 函数才会执行后续的 Middleware。

    RenderMiddleware

    如果只需要处理页面渲染的前后执行逻辑,modern.js 也提供了渲染中间件,使用姿势如下:

    server/modern.server.ts
    import {
      defineServerConfig,
      type MiddlewareHandler,
    } from '@modern-js/server-runtime';
    
    // 注入 render 性能指标
    const renderTiming: MiddlewareHandler = async (c, next) => {
      const start = Date.now();
    
      await next();
    
      const end = Date.now();
      c.res.headers.set('server-timing', `render; dur=${end - start}`);
    };
    
    // 修改响应体
    const modifyResBody: MiddlewareHandler = async (c, next) => {
      await next();
    
      const { res } = c;
      const text = await res.text();
      const newText = text.replace('<body>', '<body> <h3>bytedance</h3>');
    
      c.res = c.body(newText, {
        status: res.status,
        headers: res.headers,
      });
    };
    
    export default defineServerConfig({
      renderMiddlewares: [
        {
          name: 'render-timing',
          handler: renderTiming,
        },
        {
          name: 'modify-res-body',
          handler: modifyResBody,
        },
      ],
    });

    Plugin

    Modern.js 支持在自定义插件中为 Server 添加上述 Middleware 及 RenderMiddleware,使用姿势如下:

    server/plugins/server.ts
    import type { ServerPlugin } from '@modern-js/server-runtime';
    
    export default (): ServerPlugin => ({
      name: 'serverPlugin',
      setup(api) {
        api.onPrepare(() => {
          const { middlewares, renderMiddlewares } = api.getServerContext();
    
          // 注入服务端数据,供页面 dataLoader 消费
          middlewares?.push({
            name: 'server-plugin-middleware',
            handler: async (c, next) => {
              c.set('message', 'hi modern.js');
              await next();
              // ...
            },
          });
    
          // 重定向
          renderMiddlewares?.push({
            name: 'server-plugin-render-middleware',
            handler: async (c, next) => {
              const user = getUser(c.req);
              if (!user) {
                return c.redirect('/login');
              }
    
              await next();
            },
          });
        });
      },
    });
    server/modern.server.ts
    import { defineServerConfig } from '@modern-js/server-runtime';
    import serverPlugin from './plugins/serverPlugin';
    
    export default defineServerConfig({
      plugins: [serverPlugin()],
    });
    src/routes/page.data.ts
    import { useHonoContext } from '@modern-js/server-runtime';
    import { defer } from '@modern-js/runtime/router';
    
    export default () => {
      const ctx = useHonoContext();
      // SSR 场景消费服务端注入的数据
      const message = ctx.get('message');
    
      // ...
    };

    onError

    onError 是一个全局错误处理函数,用于捕获和处理 Modern.js server 中的所有未捕获错误。通过自定义 onError 函数,开发者可以统一处理不同类型的错误,返回自定义的错误响应,实现错误日志记录、错误分类处理等功能。

    以下是一个基本的 onError 配置示例:

    server/modern.server.ts
    import { defineServerConfig } from '@modern-js/server-runtime';
    
    export default defineServerConfig({
      onError: (err, c) => {
        // 记录错误日志
        console.error('Server error:', err);
    
        // 根据不同的错误类型返回不同的响应
        if (err instanceof SyntaxError) {
          return c.json({ error: 'Invalid JSON' }, 400);
        }
    
        // 根据请求路径定制 bff 异常响应
        if (c.req.path.includes('/api')) {
          return c.json({ message: 'API error occurred' }, 500);
        }
    
        return c.text('Internal Server Error', 500);
      },
    });