路由系统

19 阅读4分钟

NestJS 源码解析:路由系统

深入 RouterExplorer 和 RouterExecutionContext,揭秘请求的分发与处理。

路由注册流程

NestApplication.listen()
        ↓
RoutesResolver.resolve()
        ↓
RouterExplorer.explore()
        ↓
HttpAdapter.bindHandler()

请求作用域处理

当控制器或其依赖是请求作用域时,NestJS 会为每个请求创建新实例:

// packages/core/router/router-explorer.ts
private applyCallbackToRouter(...) {
  const isRequestScoped = !instanceWrapper.isDependencyTreeStatic();
  
  const proxy = isRequestScoped
    ? this.createRequestScopedHandler(/*...*/)  // 请求作用域
    : this.createCallbackProxy(/*...*/);        // 静态作用域
}

public createRequestScopedHandler(
  instanceWrapper: InstanceWrapper,
  requestMethod: RequestMethod,
  moduleRef: Module,
  moduleKey: string,
  methodName: string,
) {
  const { instance } = instanceWrapper;
  const collection = moduleRef.controllers;
  const isTreeDurable = instanceWrapper.isDependencyTreeDurable();

  return async <TRequest, TResponse>(req: TRequest, res: TResponse, next: () => void) => {
    try {
      // 获取或创建请求上下文 ID
      const contextId = this.getContextId(req, isTreeDurable);
      
      // 为当前请求加载控制器实例
      const contextInstance = await this.injector.loadPerContext(
        instance,
        moduleRef,
        collection,
        contextId,
      );
      
      // 创建并执行代理
      await this.createCallbackProxy(
        contextInstance,
        contextInstance[methodName],
        methodName,
        moduleKey,
        requestMethod,
        contextId,
        instanceWrapper.id,
      )(req, res, next);
    } catch (err) {
      // 异常处理...
    }
  };
}

关键点:

  • isDependencyTreeStatic() 判断整个依赖树是否都是静态的
  • isDependencyTreeDurable() 判断是否是持久化作用域
  • loadPerContext() 为特定上下文加载实例

RoutesResolver

解析所有控制器的路由:

// packages/core/router/routes-resolver.ts
export class RoutesResolver {
  public resolve(applicationRef: HttpServer, globalPrefix: string) {
    const modules = this.container.getModules();

    modules.forEach(({ controllers, metatype }, moduleName) => {
      // 获取模块路径前缀
      const modulePath = this.getModulePathMetadata(metatype);

      // 遍历控制器
      controllers.forEach((wrapper, token) => {
        const { instance, metatype } = wrapper;

        // 获取控制器路径
        const paths = this.routerExplorer.extractRouterPath(metatype);
        const host = this.getHostMetadata(metatype);

        // 探索并注册路由
        paths.forEach(path => {
          this.routerExplorer.explore(
            wrapper,
            moduleName,
            applicationRef,
            host,
            {
              ctrlPath: path,
              modulePath,
              globalPrefix,
            },
          );
        });
      });
    });
  }
}

RouterExplorer

探索控制器的路由方法:

// packages/core/router/router-explorer.ts
export class RouterExplorer {
  public explore(
    instanceWrapper: InstanceWrapper,
    moduleKey: string,
    httpAdapterRef: HttpServer,
    host: string | RegExp | Array<string | RegExp>,
    routePathMetadata: RoutePathMetadata,
  ) {
    const { instance } = instanceWrapper;

    // 扫描所有路由方法
    const routerPaths = this.pathsExplorer.scanForPaths(instance);

    // 注册到 HTTP 适配器
    this.applyPathsToRouterProxy(
      httpAdapterRef,
      routerPaths,
      instanceWrapper,
      moduleKey,
      routePathMetadata,
      host,
    );
  }

  // 注册路由
  public applyPathsToRouterProxy(
    router: HttpServer,
    routeDefinitions: RouteDefinition[],
    instanceWrapper: InstanceWrapper,
    moduleKey: string,
    routePathMetadata: RoutePathMetadata,
    host: string | RegExp | Array<string | RegExp>,
  ) {
    routeDefinitions.forEach(routeDefinition => {
      this.applyCallbackToRouter(
        router,
        routeDefinition,
        instanceWrapper,
        moduleKey,
        routePathMetadata,
        host,
      );
    });
  }

  // 绑定处理函数
  private applyCallbackToRouter(
    router: HttpServer,
    routeDefinition: RouteDefinition,
    instanceWrapper: InstanceWrapper,
    moduleKey: string,
    routePathMetadata: RoutePathMetadata,
    host: string | RegExp | Array<string | RegExp>,
  ) {
    const { path, requestMethod, targetCallback, methodName } = routeDefinition;

    // 创建执行上下文
    const executionContext = this.executionContextCreator.create(
      instanceWrapper,
      targetCallback,
      methodName,
      moduleKey,
      requestMethod,
    );

    // 创建代理处理函数
    const proxy = this.createCallbackProxy(
      instanceWrapper,
      executionContext,
      methodName,
      moduleKey,
      requestMethod,
    );

    // 构建完整路径
    const fullPath = this.routePathFactory.create(routePathMetadata, requestMethod);

    // 注册到路由器
    this.routerMethodFactory
      .get(router, requestMethod)
      .call(router, fullPath, proxy);

    this.logger.log(ROUTE_MAPPED_MESSAGE(fullPath, requestMethod));
  }
}

RouterExecutionContext

创建路由执行上下文,组装完整的请求处理管道:

// packages/core/router/router-execution-context.ts
export class RouterExecutionContext {
  public create(
    instance: Controller,
    callback: (...args: any[]) => unknown,
    methodName: string,
    moduleKey: string,
    requestMethod: RequestMethod,
    contextId = STATIC_CONTEXT,
    inquirerId?: string,
  ) {
    const contextType: ContextType = 'http';
    
    // 获取元数据
    const { argsLength, fnHandleResponse, paramtypes, getParamsMetadata, httpStatusCode, responseHeaders } = 
      this.getMetadata(instance, callback, methodName, moduleKey, requestMethod, contextType);

    // 创建管道
    const pipes = this.pipesContextCreator.create(instance, callback, moduleKey, contextId, inquirerId);
    
    // 创建守卫
    const guards = this.guardsContextCreator.create(instance, callback, moduleKey, contextId, inquirerId);
    
    // 创建拦截器
    const interceptors = this.interceptorsContextCreator.create(instance, callback, moduleKey, contextId, inquirerId);

    // 创建守卫执行函数
    const fnCanActivate = this.createGuardsFn(guards, instance, callback, contextType);
    
    // 创建管道执行函数
    const fnApplyPipes = this.createPipesFn(pipes, paramsOptions);

    // 返回请求处理函数
    return async <TRequest, TResponse>(req: TRequest, res: TResponse, next: Function) => {
      const args = this.contextUtils.createNullArray(argsLength);
      
      // 1. 执行守卫
      fnCanActivate && (await fnCanActivate([req, res, next]));

      // 2. 设置响应状态和头
      this.responseController.setStatus(res, httpStatusCode);
      hasCustomHeaders && this.responseController.setHeaders(res, responseHeaders);

      // 3. 执行拦截器(包含管道和处理器)
      const result = await this.interceptorsConsumer.intercept(
        interceptors,
        [req, res, next],
        instance,
        callback,
        handler(args, req, res, next),  // 内部执行管道和处理器
        contextType,
      );
      
      // 4. 处理响应
      await fnHandleResponse(result, res, req);
    };
  }
}

关键点:

  • 按顺序创建 Guards、Interceptors、Pipes
  • 守卫在拦截器之前执行
  • 管道在路由处理器之前执行(在拦截器内部)

## 请求处理流程

Request ↓ ┌─────────────────────────────────────┐ │ 1. Guards (守卫) │ │ canActivate() → true/false │ └─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────┐ │ 2. Interceptors (拦截器 - before) │ │ intercept() → Observable │ └─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────┐ │ 3. Pipes (管道) │ │ transform() → value │ └─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────┐ │ 4. Route Handler (路由处理器) │ │ controller.method() │ └─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────┐ │ 5. Interceptors (拦截器 - after) │ │ Observable.pipe() │ └─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────┐ │ 6. Exception Filters (异常过滤器) │ │ catch() → response │ └─────────────────────────────────────┘ ↓ Response


## GuardsConsumer

执行守卫:

```typescript
// packages/core/guards/guards-consumer.ts
export class GuardsConsumer {
  public async tryActivate(
    guards: CanActivate[],
    args: unknown[],
    instance: Controller,
    callback: (...args: unknown[]) => unknown,
    type?: string,
  ): Promise<boolean> {
    if (!guards || isEmpty(guards)) {
      return true;
    }

    const context = this.createContext(args, instance, callback);
    context.setType(type);

    // 依次执行守卫
    for (const guard of guards) {
      const result = guard.canActivate(context);

      // 处理同步/异步/Observable 结果
      if (typeof result === 'boolean') {
        if (!result) return false;
        continue;
      }

      if (await this.pickResult(result)) {
        continue;
      }
      return false;
    }
    return true;
  }
}

InterceptorsConsumer

执行拦截器:

// packages/core/interceptors/interceptors-consumer.ts
export class InterceptorsConsumer {
  public async intercept(
    interceptors: NestInterceptor[],
    args: unknown[],
    instance: Controller,
    callback: (...args: unknown[]) => unknown,
    next: () => Promise<unknown>,
    type?: string,
  ): Promise<unknown> {
    if (isEmpty(interceptors)) {
      return next();
    }

    const context = this.createContext(args, instance, callback);
    context.setType(type);

    // 构建拦截器链
    const nextFn = async (i = 0) => {
      if (i >= interceptors.length) {
        return defer(() => this.transformDeferred(next));
      }

      const handler: CallHandler = {
        handle: () => defer(() => nextFn(i + 1)).pipe(mergeAll()),
      };

      return interceptors[i].intercept(context, handler);
    };

    return defer(() => nextFn()).pipe(mergeAll());
  }
}

PipesConsumer

执行管道:

// packages/core/pipes/pipes-consumer.ts
export class PipesConsumer {
  public async apply(
    value: unknown,
    { metatype, type, data }: ArgumentMetadata,
    pipes: PipeTransform[],
  ) {
    return this.applyPipes(value, { metatype, type, data }, pipes);
  }

  public async applyPipes(
    value: unknown,
    metadata: { metatype: any; type?: any; data?: any },
    transforms: PipeTransform[],
  ) {
    // 依次执行管道
    return transforms.reduce(async (deferredValue, pipe) => {
      const val = await deferredValue;
      const result = pipe.transform(val, metadata);
      return result;
    }, Promise.resolve(value));
  }
}

参数解析

RouteParamsFactory

// packages/core/router/route-params-factory.ts
export class RouteParamsFactory {
  public exchangeKeyForValue(
    key: RouteParamtypes,
    data: string | object | any,
    { req, res, next }: { req: any; res: any; next: Function },
  ) {
    switch (key) {
      case RouteParamtypes.REQUEST:
        return req;
      case RouteParamtypes.RESPONSE:
        return res;
      case RouteParamtypes.NEXT:
        return next;
      case RouteParamtypes.BODY:
        return data ? req.body?.[data] : req.body;
      case RouteParamtypes.PARAM:
        return data ? req.params?.[data] : req.params;
      case RouteParamtypes.QUERY:
        return data ? req.query?.[data] : req.query;
      case RouteParamtypes.HEADERS:
        return data ? req.headers?.[data.toLowerCase()] : req.headers;
      case RouteParamtypes.IP:
        return req.ip;
      case RouteParamtypes.HOST:
        return req.hostname;
      default:
        return null;
    }
  }
}

异常处理

ExceptionsHandler

// packages/core/exceptions/exceptions-handler.ts
export class ExceptionsHandler {
  public handle(
    exception: Error | HttpException | any,
    host: ArgumentsHost,
  ): void {
    // 尝试使用自定义过滤器
    for (const filter of this.filters) {
      if (this.isExceptionOfType(exception, filter.exceptionMetatypes)) {
        filter.func(exception, host);
        return;
      }
    }

    // 使用默认处理
    this.invokeCustomFilters(exception, host);
  }
}

版本控制

@Controller({
  path: 'cats',
  version: '1',
})
export class CatsControllerV1 {}

@Controller({
  path: 'cats',
  version: '2',
})
export class CatsControllerV2 {}

版本路由处理:

// packages/core/router/router-explorer.ts
private applyVersionFilter(
  router: HttpServer,
  routePathMetadata: RoutePathMetadata,
  versioningOptions: VersioningOptions,
) {
  const version = routePathMetadata.methodVersion;

  if (versioningOptions.type === VersioningType.URI) {
    // URI 版本:/v1/cats
    return this.routePathFactory.create(routePathMetadata, requestMethod);
  }

  if (versioningOptions.type === VersioningType.HEADER) {
    // Header 版本:X-API-Version: 1
    return this.createVersionedHandler(version);
  }

  if (versioningOptions.type === VersioningType.MEDIA_TYPE) {
    // Media Type 版本:Accept: application/json;v=1
    return this.createMediaTypeVersionedHandler(version);
  }
}

总结

NestJS 路由系统的核心:

  1. RoutesResolver:解析所有控制器的路由
  2. RouterExplorer:探索路由方法,注册到 HTTP 适配器
  3. RouterExecutionContext:创建执行上下文,组装管道
  4. 请求处理管道:Guards → Interceptors → Pipes → Handler → Interceptors → Filters
  5. 参数解析:RouteParamsFactory 解析 @Body、@Query 等
  6. 版本控制:支持 URI、Header、Media Type 三种方式

下一篇我们将分析中间件机制的实现。


📦 源码位置:packages/core/router/

下一篇:NestJS 中间件机制