Nest.js中Multer规则触发异常后连接未释放问题求助
问题背景
我有一个接收单张或多张图片上传(multipart/form-data)的Nest.js应用。当违反Multer规则(如发送超过1个文件)时,Multer会抛出Too Many Files异常,该异常被全局过滤器捕获,但连接未真正终止,导致再次请求时应用挂起,需等待超时或取消请求后才能正常使用(请求仅隔次成功)。取消注释异常过滤器中的response.destroy语句强制终止连接可解决问题,但担心该方式引发后续隐患。已尝试Nest.js 9.x版本、自定义拦截器、中间件等多种方案,均出现相同问题。
相关代码
控制器代码
@Post() @UseInterceptors(FilesInterceptor('image')) public async initiate( @Res() res: Response, @UploadedFiles() images?: Array<Express.Multer.File>): Promise<void> { let retCode = HttpStatus.ACCEPTED; res.status(retCode).send(); }
MulterModule注册模块代码
@Module({ imports: [ MulterModule.registerAsync({ useClass: MulterConfig }) ], controllers: [TestController], providers: [Logger, TestService] }) export class TestModule {}
MulterConfig类代码
@Injectable() export class MulterConfig implements MulterOptionsFactory { constructor() {} public createMulterOptions(): MulterModuleOptions { return { limits: { files: 1 }, fileFilter: (req, file, callback) => { console.log(`Filetype is: ${file.mimetype}`); if(file.mimetype === 'image/png') { callback(null, true); } else { callback(new BadRequestException('asdfasdf'), false); } } } } }
全局异常过滤器注册(main.ts)
app.useGlobalFilters(new TestExceptionFilter());
异常过滤器代码
import { Catch, ExceptionFilter, ArgumentsHost } from "@nestjs/common"; import { Request, Response } from 'express'; @Catch() export class TestExceptionFilter implements ExceptionFilter { catch(exception: any, host: ArgumentsHost) { console.log('in exception filter'); let status = 500; const ctx = host.switchToHttp(); const response = ctx.getResponse<Response>(); response.status(status).send(); //response.destroy(new Error('forcing stream destruction')); } }
项目依赖版本
"@nestjs/axios": "^3.0.0", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.0.0", "@nestjs/core": "^10.0.0", "@nestjs/mapped-types": "*", "@nestjs/mongoose": "^10.0.1", "@nestjs/passport": "^10.0.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/terminus": "^10.0.1", "jwks-rsa": "^3.0.1", "mongoose": "^7.4.0", "nest-winston": "^1.9.3", "passport": "^0.6.0", "passport-jwt": "^4.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "winston": "^3.10.0"
问题原因
核心原因是Multer处理multipart/form-data请求时,若中途抛出异常,请求的数据流并未被完全消费。当你在异常过滤器中仅调用response.status(status).send()时,Express会尝试发送响应,但客户端仍在继续上传文件(请求流未被终止),导致连接处于半开状态。Node.js的HTTP连接池无法释放该连接,后续新请求会被阻塞等待可用连接,最终出现请求隔次成功的现象。
合理解决方案
方案1:在异常过滤器中主动终止请求流(推荐)
在异常过滤器中不仅发送错误响应,还要主动销毁请求的底层TCP连接,强制客户端停止上传并释放连接资源:
@Catch() export class TestExceptionFilter implements ExceptionFilter { catch(exception: any, host: ArgumentsHost) { console.log('in exception filter'); const status = exception instanceof BadRequestException ? 400 : 500; const ctx = host.switchToHttp(); const request = ctx.getRequest<Request>(); const response = ctx.getResponse<Response>(); // 终止底层TCP连接,阻止客户端继续发送数据 if (request.socket) { request.socket.destroy(); } response.status(status).json({ statusCode: status, message: exception.message || 'Internal Server Error' }); } }
方案2:在Multer配置中添加onError回调提前处理
直接在Multer的配置里添加异常处理回调,在Multer抛出异常时立即终止连接,避免异常流走到全局过滤器:
@Injectable() export class MulterConfig implements MulterOptionsFactory { public createMulterOptions(): MulterModuleOptions { return { limits: { files: 1 }, fileFilter: (req, file, callback) => { console.log(`Filetype is: ${file.mimetype}`); if(file.mimetype === 'image/png') { callback(null, true); } else { callback(new BadRequestException('仅支持PNG格式图片'), false); } }, onError: (err, req, res, next) => { // 直接终止请求连接 req.socket.destroy(); next(err); } }; } }
方案3:使用Nest.js默认异常过滤器
如果你不需要自定义全局异常处理逻辑,可以移除自定义的TestExceptionFilter,使用Nest.js内置的异常过滤器。默认过滤器在处理Multer异常时,会自动处理请求流的释放(Nest.js底层已做相关优化)。
效果验证
修改后,当上传不符合规则的文件时,客户端会立即收到错误响应,连接被正常终止,后续请求不会再出现挂起情况。
内容的提问来源于stack exchange,提问作者Lee Anderson

