Nestjs AOP:adapters decorators filters guards intercreptors pipes
Date:
NestJS 的架构本质:基于 AOP(面向切面编程)和依赖注入,通过 “关注点分离” 构建分层、可扩展的服务端框架。
这些组件围绕HTTP 请求生命周期各司其职,像工厂流水线的不同环节一样协作,既解耦又统一。
一个 HTTP 请求到达 NestJS 后,会依次经过:
适配器(底层接入)→ 守卫(权限校验)→ 管道(输入处理)→ 拦截器(请求增强)→ 控制器 / 服务(业务逻辑)→ 拦截器(响应增强)→ 过滤器(异常处理)→ 适配器(响应返回)

每个组件都是 “切面”,负责单一职责,最终让业务逻辑聚焦核心功能。
| 架构类别 | 作用(高层概念) | 包含的 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