内容提要
本文介绍 NestJS 的文件上传、拦截器、异常过滤器、管道和守卫。文件上传使用 Multer 中间件及 FileInterceptor 等拦截器,支持单文件、多文件、任意字段和自定义字段,可配置存储与静态目录,并返回或下载文件。拦截器基于 RxJS 处理请求响应,异常过滤器和管道负责异常处理与数据验证转换,守卫用于鉴权。
延伸解读
文件上传拦截器的选择与配置
NestJS 基于 Multer 提供了多种文件上传拦截器,分别对应不同场景:FileInterceptor 处理单文件,FilesInterceptor 处理单字段多文件,AnyFilesInterceptor 处理任意字段,FileFieldsInterceptor 处理多字段多文件,NoFilesInterceptor 则用于拒绝文件上传。配置时可通过 MulterOptions 设置存储目录、文件名、文件大小限制等。若多个路由共享配置,可使用 MulterModule.register() 统一管理,避免重
拦截器与异常过滤器的职责边界
拦截器基于 RxJS 实现,适合在请求前后添加逻辑,如格式化响应、记录处理时间、超时与错误处理。但文章强调,鉴权应交给守卫,而非拦截器。异常过滤器负责捕获未处理的异常并返回友好响应,一旦绑定自定义过滤器,就会覆盖内置的异常处理层,需要手动构造响应。注意,路由处理函数中抛出的异常可被拦截器的 catchError 捕获,而不会进入异常过滤器。
管道验证的两种实现路径
管道用于转换和验证参数,Nest 内置了 ParseIntPipe 等转换管道和基于 class-validator 的 ValidationPipe。文章展示了两种自定义验证方式:一是使用 class-validator 装饰器定义 DTO,二是使用 Zod 等模式验证库。两者都需在管道中调用验证方法,失败时抛出 BadRequestException。全局管道可统一应用验证,但需注意可选属性应启用 skipUndefinedProperties,且多个管道按顺序执行。
守卫与元数据实现角色控制
守卫用于鉴权,在中间件之后、管道和拦截器之前执行。通过 @SetMetadata 设置路由允许的角色,守卫中使用 Reflector 获取元数据并比对用户角色。文章还介绍了自定义元数据装饰器、组合装饰器以及 Reflector 的多层次元数据获取方法(getAll、getAllAndMerge、getAllAndOverride)。守卫应保持单一职责,仅返回布尔值决定是否放行,不建议修改请求响应对象。
Q&A
NestJS 中如何处理文件上传?
NestJS 内置了 Express 的 Multer 中间件,用于处理 multipart/form-data 类型的表单数据。可以使用 FileInterceptor 处理单文件上传,FilesInterceptor 处理多文件上传,AnyFilesInterceptor 处理任意字段上传,FileFieldsInterceptor 处理自定义字段上传。通过 @UploadedFile() 和 @UploadedFiles() 装饰器获取文件对象。
NestJS 拦截器有什么作用?如何实现一个简单的拦截器?
拦截器用于在函数执行前后绑定额外逻辑、转换返回结果或异常、扩展函数行为等。实现一个拦截器需要创建一个带有 @Injectable() 装饰器的类,实现 NestInterceptor 接口,定义 intercept 方法,该方法接收 ExecutionContext 和 CallHandler,返回 Observable。例如:@Injectable() export class ConverterInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { return next.handle(); } }
NestJS 中如何自定义异常过滤器?
自定义异常过滤器需要创建一个带有 @Catch() 装饰器的类,实现 ExceptionFilter 接口,定义 catch 方法。@Catch() 可以指定捕获的异常类型,不传参数则捕获所有异常。在 catch 方法中,可以通过 ArgumentsHost 获取请求和响应对象,然后自定义响应。例如:@Catch(HttpException) export class ErrorFilter implements ExceptionFilter { catch(exception: HttpException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const res = ctx.getResponse<Response>(); res.status(exception.getStatus()).json(exception.getResponse()); } }
NestJS 管道的作用是什么?有哪些内置管道?
管道用于转换输入数据为所需形式,或验证输入数据是否符合要求,若失败则抛出异常。内置管道包括:ValidationPipe(验证)、ParseIntPipe(转整数)、ParseFloatPipe(转浮点数)、ParseBoolPipe(转布尔值)、ParseArrayPipe(转数组)、ParseUUIDPipe(转UUID)、ParseEnumPipe(转枚举)、DefaultValuePipe(默认值)、ParseFilePipe(文件对象)。
NestJS 守卫如何实现基于角色的访问控制(RBAC)?
守卫可以通过 @SetMetadata() 设置路由元数据(如允许的角色),然后使用 Reflector 获取元数据,在 canActivate 方法中对比用户角色。例如:@SetMetadata('roles', ['admin']) 设置元数据,守卫中通过 this.reflector.get<string[]>('roles', context.getHandler()) 获取角色,然后检查请求中的用户角色是否在允许列表中。
NestJS 中如何设置静态资源目录以访问上传的文件?
在 main.ts 中,将 NestFactory.create 的泛型设置为 NestExpressApplication,然后使用 app.useStaticAssets() 设置静态资源目录。例如:app.useStaticAssets(join(__dirname, '..', 'public')); 这样可以通过 URL 访问 public 目录下的文件。还可以在 nest-cli.json 中配置 compilerOptions.assets 来复制静态资源到输出目录。