You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用TypeScript编写类Express中间件的Node.js函数?

纯Node.js + TypeScript 实现类Express中间件的最佳实践

一、先统一核心类型定义(TS强约束基础)

脱离Express的类型依赖,基于Node原生HTTP对象定义通用上下文和中间件类型,确保跨环境兼容性:

import { IncomingMessage, ServerResponse } from 'http';

// 定义Next函数类型,支持同步/异步流转
type Next = () => Promise<void> | void;

// 通用上下文:基于Node原生对象,预留扩展字段
interface Context {
  req: IncomingMessage;
  res: ServerResponse;
  // 允许添加自定义属性(如用户信息、请求ID等)
  [key: string]: any;
}

// 标准中间件类型:对齐Express的(req,res,next)结构,但使用自定义Context
type Middleware = (ctx: Context, next: Next) => Promise<void> | void;

二、装饰器方案的落地规范

你的装饰器思路完全可行,装饰器可用于封装通用逻辑(如错误捕获、日志、权限校验),让业务中间件专注核心逻辑。以下是两种常见装饰器的实现规范:

1. 中间件增强装饰器(包装已有中间件)

比如实现错误捕获装饰器,自动处理中间件抛出的异常,避免重复编写try/catch:

// 错误捕获装饰器
const catchErrors = (middleware: Middleware): Middleware => {
  return async (ctx, next) => {
    try {
      await middleware(ctx, next);
    } catch (err) {
      // 统一错误处理逻辑:返回500响应、记录日志
      ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
      ctx.res.end(JSON.stringify({ error: (err as Error).message }));
    }
  };
};

// 使用示例:给业务中间件添加错误捕获能力
const userMiddleware: Middleware = async (ctx, next) => {
  // 模拟业务报错
  throw new Error('获取用户信息失败');
  await next();
};

// 增强后的中间件
const enhancedUserMiddleware = catchErrors(userMiddleware);

2. 类式中间件的装饰器(用类组织中间件逻辑)

如果偏好用类管理中间件(类似NestJS风格),可通过方法装饰器标记中间件方法,再转为标准Middleware类型:

// 装饰器:标记类方法为中间件
const Middleware = () => {
  return (target: any, propertyKey: string) => {
    // 把方法挂载到类的静态属性,方便后续收集
    target.__middlewares = target.__middlewares || [];
    target.__middlewares.push(target[propertyKey]);
  };
};

// 定义中间件类
class AuthMiddleware {
  @Middleware()
  async checkToken(ctx: Context, next: Next) {
    const token = ctx.req.headers['authorization'];
    if (!token) {
      ctx.res.writeHead(401, { 'Content-Type': 'application/json' });
      ctx.res.end(JSON.stringify({ error: '未授权' }));
      return;
    }
    // 验证token逻辑...
    ctx.user = { id: 123 }; // 将用户信息挂载到上下文
    await next();
  }
}

// 将类中的中间件方法转为标准Middleware数组
const authMiddlewareList = AuthMiddleware.__middlewares as Middleware[];

三、中间件执行器的实现(核心调度逻辑)

要让中间件像Express一样按顺序执行,需实现一个轻量调度器,这是脱离Express后的核心部分,确保跨环境可用:

// 中间件执行器:按顺序执行中间件队列
const runMiddlewares = async (ctx: Context, middlewares: Middleware[]) => {
  let index = 0;

  // 定义next函数,控制中间件流转
  const next: Next = async () => {
    if (index >= middlewares.length) return;
    const currentMiddleware = middlewares[index++];
    await currentMiddleware(ctx, next);
  };

  await next();
};

// 使用示例:创建Node HTTP服务器,绑定中间件
import { createServer } from 'http';

const server = createServer(async (req, res) => {
  const ctx: Context = { req, res };
  // 中间件队列
  const middlewares = [
    catchErrors(async (ctx, next) => {
      console.log('第一个中间件:日志记录');
      await next();
    }),
    ...authMiddlewareList,
    async (ctx, next) => {
      // 业务处理逻辑
      ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
      ctx.res.end(JSON.stringify({ message: '请求成功', user: ctx.user }));
      await next();
    }
  ];

  await runMiddlewares(ctx, middlewares);
});

server.listen(3000, () => console.log('服务器运行在3000端口'));

四、跨环境适配的注意事项

  1. 避免依赖特定框架API:全程使用Node原生http模块的IncomingMessage和ServerResponse,如需解析请求体,可使用querystring、raw-body等轻量库,避免引入Express冗余依赖。
  2. AWS环境优化:在AWS Lambda中无需创建HTTP服务器,只需将中间件执行器适配Lambda事件模型:
    import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
    import { IncomingMessage, ServerResponse } from 'http';
    import { Readable } from 'stream';
    
    // 将Lambda事件转为Node原生IncomingMessage
    const eventToRequest = (event: APIGatewayProxyEvent): IncomingMessage => {
      const req = new IncomingMessage(Readable.from(Buffer.from(event.body || '')));
      req.method = event.httpMethod;
      req.url = event.path;
      req.headers = event.headers as any;
      return req;
    };
    
    // 将ServerResponse转为Lambda返回结果
    const responseToResult = (res: ServerResponse): APIGatewayProxyResult => {
      // 需扩展ServerResponse的getBody方法收集响应内容,或通过回调实现
      return {
        statusCode: res.statusCode || 200,
        headers: res.getHeaders() as any,
        body: res.getBody() || ''
      };
    };
    
    // Lambda处理函数
    export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
      const req = eventToRequest(event);
      const res = new ServerResponse(req);
      const ctx: Context = { req, res };
    
      await runMiddlewares(ctx, middlewares);
    
      return responseToResult(res);
    };
    
  3. 保持中间件单一职责:每个中间件只做一件事(如日志、权限、请求体解析),便于复用和调试。
  4. 强制异步支持:所有中间件需支持异步(返回Promise或使用async/await),执行器需正确处理异步流转,避免回调地狱。

五、装饰器思路的验证

你的装饰器思路是合理且高效的,核心优势包括:

  • 封装通用逻辑,减少重复代码;
  • 保持业务中间件的简洁性;
  • 符合TS类型约束,可读性更强;
  • 完全脱离Express依赖,资源占用更低,适配AWS等无服务器环境。

内容的提问来源于stack exchange,提问作者Irfandy J.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 02:47:52