• home > webfront > server > NestJS >

    Nestjs AOP:adapters decorators filters guards intercreptors pipes

    Date:

    NestJS 的架构本质:基于 AOP(面向切面编程)和依赖注入,通过 “关注点分离” 构建分层、可扩展的服务端框架。这些组件围绕HTTP 请

    NestJS 的架构本质:基于 AOP(面向切面编程)和依赖注入,通过 “关注点分离” 构建分层、可扩展的服务端框架

    这些组件围绕HTTP 请求生命周期各司其职,像工厂流水线的不同环节一样协作,既解耦又统一。

    一个 HTTP 请求到达 NestJS 后,会依次经过:

    适配器(底层接入)→ 守卫(权限校验)→ 管道(输入处理)→ 拦截器(请求增强)→ 控制器 / 服务(业务逻辑)→ 拦截器(响应增强)→ 过滤器(异常处理)→ 适配器(响应返回)

    52772982-d76b8c00-3074-11e9-96a1-1ec9e4dc97f8.png

    每个组件都是 “切面”,负责单一职责,最终让业务逻辑聚焦核心功能。

    架构类别作用(高层概念)包含的 NestJS 元素
    I. 请求生命周期控制管理请求到达和响应发出的完整流程,实现权限、验证、异常处理等核心 AOP 功能。Guards, Interceptors, Filters
    II. 数据流与转换处理请求/响应数据在进入或离开业务逻辑之前/之后的结构、类型、校验和转换。Pipes, Interceptors (部分)
    III. 框架与环境集成桥接 NestJS 框架与底层 HTTP 库(如 Express/Fastify)或外部系统。Adapters, Decorators (部分)

    适配器 (Adapters)与装饰器 (Decorators),这属于框架底层的设计,用于提高灵活性和扩展性。

    框架与环境集成

    这属于框架底层的设计,用于提高灵活性和扩展性。

    Adapter (适配器)

    Adapters(适配器)的作用是适配不同的底层运行环境

    类比:流水线的 “接口转换器”,不管原料从卡车、火车还是轮船运来,都能统一接入流水线

    • HTTP 适配器:让 NestJS 能基于 Express(默认)或 Fastify 运行,上层 API 不变;

    • WebSocket 适配器:适配 Socket.io、WS 等;

    • 微服务适配器:适配 Redis、Kafka 等微服务通信协议。

    适配器是NestJS架构中最为底层的一个抽象概念,它本身不是一个你可以直接使用@Injectable()装饰的类,而是框架与底层HTTP服务器(如Express或Fastify)之间的一个连接层。

    • 设计初衷为了实现框架无关性。NestJS 的核心逻辑(如依赖注入、模块系统)不直接依赖于某个特定的HTTP服务器库。适配器在这中间做了翻译工作,将NestJS的请求/响应对象转换成底层库(如Express、Fastify)能理解的对象,反之亦然。

    • 切入点: 框架启动时。

    • 职责:充当抽象屏障。这意味着你可以通过更换适配器(例如,从默认的基于Express的适配器切换到基于Fastify的适配器),来获得不同底层HTTP库的性能特性和API,而无需重写你的业务逻辑。

    • 架构价值用适配器模式解耦框架核心和底层实现,让 NestJS 能无缝切换底层引擎,同时保持上层开发体验一致。

    Decorator (装饰器)

    装饰器是NestJS实现声明式编程和元数据反射的语法基石。

    类比:给流水线的 “产品” 贴标签,流水线自动知道该怎么处理它

    它们不是NestJS独有的概念,而是利用了TypeScript的特性,但NestJS极大地扩展了其应用场景。

    • 设计初衷通过一种优雅、可读性高的方式,为类、方法、参数或属性添加元数据。框架在启动时或运行时能够读取这些元数据,从而理解你的代码意图。

    • 切入点: 编译时和运行时。

    • 职责:定义路由 (@Get(), @Post()),标识依赖注入的提供者 (@Injectable()),标记模块结构 (@Module()),获取请求参数 (@Param(), @Body()),设置权限 (@SetMetadata()) 等。

      可以说,你几乎所有的代码结构都是通过装饰器来告知NestJS如何组织的

    • 架构价值用声明式语法替代繁琐的配置,实现元编程,让框架自动识别和组装组件(比如依赖注入的识别、路由的注册)。

    示例(自定义装饰器)

    import { SetMetadata } from '@nestjs/common';
    export const Roles = (...roles: string[]) => SetMetadata('roles', roles);


    Middleware (中间件)

    中间件是NestJS对Express/Koa等底层库中间件概念的封装。它可以是一个函数,也可以是一个实现了 NestMiddleware接口的类。

    • 设计初衷:在路由被处理之前,对请求和响应对象进行最基础的操作。它执行在守卫、管道等所有组件之前。

    • 职责:处理诸如请求日志记录、跨域资源共享(CORS)、请求体压缩、请求限流等基础任务。中间件可以访问请求和响应对象,以及下一个中间件函数。与守卫不同,中间件不知道执行上下文(即最终由哪个控制器和方法来处理请求)

    在nestjs中的middle完全跟express的中间件一摸一样。不仅如此,我们还可以直接使用express中的中间件,比如在我的应用中需要处理core跨域

    使用:

    import * as cors from 'cors';
    async function bootstrap() {
      onst app = await NestFactory.create(/* 创建app的业务逻辑*/)
      app.use(cors({
        origin:'http://localhost:8080',
        credentials:true
      }));
      await app.listen(3000)
    }
    bootstrap();

    在上述的代码中我们可以直接通过app.use来使用core这个express中的中间件。从而使得server端支持core跨域等。

    初此之外,跟nestjs的中间件也完全保留了express中的中间件的特点:

    • 在中间件中接受response和request作为参数,并且可以修改请求对象request和结果返回对象response

    • 可以结束对于请求的处理,直接将请求的结果返回,也就是说可以在中间件中直接res.send等。

    • 在该中间件处理完毕后,如果没有将请求结果返回,那么可以通过next方法,将中间件传递给下一个中间件处理。

    在nestjs中,中间件跟express中完全一样,除了可以复用express中间件外,在nestjs中针对某一个特定的路由来使用中间件也十分的方便:

    class ApplicationModule implements NestModule {
      configure(consumer: MiddlewareConsumer) {
        consumer
          .apply(LoggerMiddleware)
          .forRoutes('cats');
      }
    }

    上面就是对于特定的路由url为/cats的时候,使用LoggerMiddleware中间件。


    数据流与转换

    处理请求/响应数据在进入或离开业务逻辑之前/之后的结构、类型、校验和转换。

    专注于对请求体、查询参数、路由参数等输入数据进行校验和清洗。

    Pipe (管道)

    在请求到达业务逻辑前,对输入参数做验证(如 DTO 校验、参数类型检查)或转换(如字符串转数字),无效输入直接抛异常。

    类比:流水线的 “原料预处理环节”,确保进入生产线的原料都是合格的

    管道是一个实现了 PipeTransform接口的类,通常用 @Injectable()装饰。它专注于操作输入数据。

    • 设计初衷:处理数据转换和数据验证,在参数被处理函数接收之前,NestJS 会将参数交给 Pipe 处理。

    • 切入点: 在参数被处理函数接收之前,NestJS 会将参数交给 Pipe 处理。

    • 职责

      • 转换:将输入数据转换成期望的表单(例如,将字符串 "123"转换为数字 123)。

      • 验证:对输入数据进行校验,如果数据有效,就直接传递;如果无效,则抛出异常。

    • 架构价值把输入处理和业务逻辑解耦,符合单一职责原则;支持全局 / 局部管道,实现输入规则的复用。

    管道通常作用于控制器方法的参数。

    NestJS内置了多个开箱即用的管道,如 ValidationPipe(通常与 class-validator库一起用于DTO验证)和 ParseIntPipe。

    app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));

    比如我们对于一个请求中的某些参数,需要校验或者转化参数的类型:

    @Injectable()
    export class ParseIntPipe implements PipeTransform<string, number> {
      transform(value: string, metadata: ArgumentMetadata): number {
        const val = parseInt(value, 10);
        if (isNaN(val)) {
          throw new BadRequestException('Validation failed');
        }
        return val;
      }
    }

    上述的ParseIntPipe就可以把参数转化成十进制的整型数字。我们可以这样使用:

    @Get(':id')
    async findOne(@Param('id', new ParseIntPipe()) id) {
      return await this.catsService.findOne(id);
    }

     对于get请求中的参数id,调用new ParseIntPipe方法来将id参数转化成十进制的整数。


    Interceptor (拦截器)

    拦截器是一个实现了 NestInterceptor接口的类,用 @Injectable()装饰。它的灵感来源于面向切面编程(AOP)。

    能在请求前后做操作:

    • 请求前:日志记录、请求参数修改、缓存预热;

    • 请求后:响应数据格式化、耗时统计、异常包装;

      甚至可以用map()/catchError()修改响应流,支持异步操作。

    类比:流水线的 “包装环节”,给产品加保护膜(请求前)、贴防伪码(响应后)

    • 设计初衷:在方法执行的前后绑定额外的逻辑。这使得它非常适合用于实现跨越多个点的横切关注点。

    • 切入点: 在 Guard 之后,在 Pipe 之前/之后。它是一个双向切面。

    • 职责:

      • 方法执行前后绑定额外逻辑:例如记录方法执行时间。

      • 转换函数返回的结果:例如将返回的数据包装成统一的响应结构 { data: ..., message: 'success' }。

      • 扩展基本函数行为

        拦截器利用了RxJS的Observable,提供了强大的功能来管理和操作响应流

    • 架构价值实现环绕增强(AOP 的 “环绕通知”),对请求 / 响应做通用处理,比如全局统一的响应格式包装、接口耗时监控。

    示例:

    @Injectable()
    export class LoggingInterceptor implements NestInterceptor {
      intercept(ctx: ExecutionContext, next: CallHandler) {
        const now = Date.now();
        return next.handle().pipe(tap(() => console.log(`Elapsed: ${Date.now()-now}ms`)));
      }
    }
    
    @Injectable()export class TimeoutInterceptor implements NestInterceptor{
      intercept(
        context:ExecutionContext,
        call$:Observable<any>
      ):Observable<any>{
        return call$.pipe(timeout(5000));
      }}


    请求生命周期控制 (AOP / 横切关注点)

    Guards, Interceptors, Filters,这三个组件是 NestJS AOP 的核心,它们像三个“哨卡”,在请求到达路由处理函数之前、之后和出错时,对请求进行干预。

    管理请求到达和响应发出的完整流程,实现权限、验证、异常处理等核心 AOP 功能。

    更多参看:https://docs.nestjs.com/faq/request-lifecycle


    Guard (守卫)

    Guards守卫,其作用就是决定一个请求是否应该被处理函数接受并处理,当然我们也可以在middleware中间件中来做请求的接受与否的处理,与middleware相比,Guards可以获得更加详细的关于请求的执行上下文信息。

    守卫是一个实现了 CanActivate接口的类,通常用 @Injectable()装饰。它专注于授权(Authorization) 逻辑

    作用是基于条件(如用户角色、JWT 令牌、IP 白名单)决定请求是否能进入后续流程,核心处理 “授权 / 认证”。

    类比:流水线入口的安检,只有符合权限的 “原料” 才能进入生产线

    @Injectable()
    export class AuthGuard implements CanActivate {
      canActivate(
        context: ExecutionContext,
      ): boolean | Promise<boolean> | Observable<boolean> {
        const request = context.switchToHttp().getRequest();
        return validateRequest(request);
      }
    }

    这里的context实现了一个ExecutionContext接口,该接口中具有丰富的执行上下文信息。

    export interface ArgumentsHost {
      getArgs<T extends Array<any> = any[]>(): T;
      getArgByIndex<T = any>(index: number): T;
      switchToRpc(): RpcArgumentsHost;
      switchToHttp(): HttpArgumentsHost;
      switchToWs(): WsArgumentsHost;
    }
    
    export interface ExecutionContext extends ArgumentsHost {
      getClass<T = any>(): Type<T>;
      getHandler(): Function;
    }

    除了ArgumentsHost中的信息外,ExecutionContext还包含了getClass用户获取对于某一个路由处理的,控制器。而getClass用于获取返回对于指定路由后台处理时的处理函数。

    对于Guards处理函数,如果返回true,那么请求会被正常的处理,如果返回false那么请求会抛出异常。

    • 设计初衷决定一个特定的请求是否应该被路由处理程序处理。它解决的是“这个用户有没有权限访问这个接口?”的问题。

    • 切入点: 在 任何 Interceptor 之前,在 Pipe 之前,但 在所有中间件之后。这是请求处理流程中最早的权限检查点。

    • 职责:在请求生命周期中,守卫在中间件之后、拦截器和管道之前执行。它可以访问 ExecutionContext(执行上下文),从而知道将要被执行的是哪个控制器和方法。守卫的 canActivate方法返回一个布尔值,若为 true则放行,若为 false则拒绝请求。常见的用法包括角色检查、权限令牌验证等。

    • 架构价值把权限控制抽象为独立切面,避免在每个控制器里重复写权限逻辑;支持全局 / 路由级守卫,灵活控制访问范围。

    示例:

    @Injectable()
    export class RolesGuard implements CanActivate {
      constructor(private reflector: Reflector, private authService: AuthService) {}
      async canActivate(ctx: ExecutionContext) {
        const roles = this.reflector.get<string[]>('roles', ctx.getHandler());
        if (!roles?.length) return true;
        const req = ctx.switchToHttp().getRequest();
        const user = req.user;
        return this.authService.userHasRoles(user, roles);
      }
    }



    Exception Filter (异常过滤器)

    异常过滤器是一个实现了 ExceptionFilter接口的类,用 @Catch(...)装饰器来指定要捕获的异常类型。

    用于捕获请求处理过程中抛出的所有异常(包括业务异常、系统异常),自定义异常响应格式(如统一返回{code: 500, msg: '错误'})。

    类比:流水线的 “次品处理站”,生产线出问题时,统一处理次品,不让整个流水线崩溃

    • 设计初衷:捕获在请求处理过程中(由控制器、服务、守卫、管道等)抛出的异常,并返回一个统一的、用户友好的响应。

    • 切入点: 只有当异常被抛出时才会触发。

    • 职责:当应用程序中抛出异常时,异常过滤器会接管,并决定如何将异常信息发送回客户端。你可以创建自定义的异常过滤器来捕获特定类型的异常(如所有 HttpException或其子类),并以特定的JSON格式或HTML页面进行响应。这有助于保持API错误响应的格式一致性

    • 架构价值把异常处理从业务逻辑中抽离,保证响应格式的统一性;支持全局 / 特定异常过滤器(如专门处理 404 的过滤器)。

    我们可以自定义一个异常过滤器,并且在这个异常过滤器中可以指定需要捕获哪些异常,并且对于这些异常应该返回什么结果等,举例一个自定义过滤器用于捕获HttpException异常的例子。

    @Catch(HttpException)
    export class HttpExceptionFilter implements ExceptionFilter {
      catch(exception: HttpException, host: ArgumentsHost) {
        const ctx = host.switchToHttp();
        const response = ctx.getResponse();
        const request = ctx.getRequest();
        const status = exception.getStatus();
    
        response
          .status(status)
          .json({
            statusCode: status,
            timestamp: new Date().toISOString(),
            path: request.url,
          });
      }
    }



    流程图


    ┌────────────────────┐
    │   客户端请求进来      │
    └───────────────────┘
             ↓
    ┌──────────────────────────────────┐
    │ 1. Adapters (Express / Fastify)   │   ← 底层服务器适配,透明
    └────────┬─────────────────────────┘
             ↓
    ┌────────────────────────────────────────────────┐
    │ 2. Guards(守卫)                               │
    │    ┌─ No → 返回 403 / 401 等 ←─────────────────┘
    │    ↓ Yes
    └────────┬─────────────────────────────────────┘
             ↓
    ┌────────────────────────────────────────────────────────────┐
    │ 3. Interceptors(拦截器) - before / use                │
    │    ┌─ 可以直接 return(比如缓存命中)→ 直接跳到 8 → 响应 │
    │    ↓
    └────────┬───────────────────────────────────────────┘
             ↓
    ┌────────────────────────────────────────────┐
    │ 4. Pipes(管道) - 参数转换 + 校验             │
    │    ┌─ 校验失败 → 抛 BadRequestException       │
    │    ↓                                      │
    └────────┬──────────────────────────────────┘
             ↓
    ┌──────────────────────────────────────────────┐
    │ 5. Controller Handler(真正的业务代码)         │
    │     执行 service、数据库等等                     │
    └────────┬─────────────────────────────────────┘
             ↓
    ┌────────────────────────────────────────────────────────────┐
    │ 6. Interceptors(拦截器) - after / next.handle() 后     │
    │    ┌─ 常用于包装统一返回结构 {code:0,data,msg}          │
    └────────┬──────────────────────────────────────────┘
             ↓
    ┌──────────────────────────────────────────────────────────────┐
    │ 7. Exception Filters(异常过滤器)                           │
    │    ←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←┘
    │      只要上面任意一层抛异常,都会跳到这里统一处理
    └────────┬──────────────────────────────────────────────────────┘
             ↓
    ┌────────────────────┐
    │   返回给客户端响应    │
    └────────────────────┘



    参考文章:

    https://docs.nestjs.com/faq/request-lifecycle

    https://github.com/hello-chinese/Blog/issues/37





    转载本站文章《Nestjs AOP:adapters decorators filters guards intercreptors pipes》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/server/nestjs/9704.html