NestJS TCP微服务异常处理:主服务无法捕获子服务异常
解决NestJS TCP微服务主服务捕获子服务异常的问题
问题根源有两个核心点:
- 子服务使用了HTTP上下文的异常过滤器,但TCP微服务属于RPC通信,无法通过HTTP上下文处理异常,导致异常没有被正确序列化传递给主服务。
- 主服务对RPC调用的异常处理方式不正确,且没有将Observable转换为Promise进行正确的异步处理。
步骤1:修复子服务的异常处理(适配RPC上下文)
1.1 创建RPC兼容的异常过滤器
替换原有的HttpExceptionFilter,创建支持RPC上下文的过滤器,将HTTP异常转换为NestJS微服务通用的RpcException,确保异常能被序列化传递:
// src/exception-filter.ts import { Catch, ArgumentsHost, ExceptionFilter } from '@nestjs/common'; import { RpcException } from '@nestjs/microservices'; import { HttpException } from '@nestjs/common'; @Catch(HttpException, RpcException) export class RpcHttpExceptionFilter implements ExceptionFilter { catch(exception: HttpException | RpcException, host: ArgumentsHost) { const ctx = host.switchToRpc(); // 切换到RPC上下文,而非HTTP if (exception instanceof HttpException) { // 将HTTP异常转换为RpcException,包含完整的错误信息 const status = exception.getStatus(); const response = exception.getResponse(); throw new RpcException({ statusCode: status, message: exception.message, details: response, }); } else if (exception instanceof RpcException) { // 直接抛出已有的RpcException throw exception; } } }
1.2 子服务启动为RPC微服务并注册全局过滤器/管道
确保子服务以TCP微服务模式启动,而非HTTP服务,并注册全局验证管道和RPC异常过滤器:
// src/main.ts import { NestFactory } from '@nestjs/core'; import { Transport } from '@nestjs/microservices'; import { AppModule } from './app.module'; import { RpcHttpExceptionFilter } from './exception-filter'; import { ValidationPipe } from '@nestjs/common'; async function bootstrap() { // 创建TCP微服务实例,而非HTTP服务 const app = await NestFactory.createMicroservice(AppModule, { transport: Transport.TCP, options: { port: 5001, }, }); // 注册验证管道,处理DTO校验异常 app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true, // 自定义校验异常格式,确保错误信息完整 exceptionFactory: (errors) => { const formattedErrors = errors.map(err => ({ field: err.property, messages: Object.values(err.constraints || {}), })); return new HttpException({ statusCode: 400, message: '参数校验失败', errors: formattedErrors, }, 400); }, })); // 注册RPC异常过滤器 app.useGlobalFilters(new RpcHttpExceptionFilter()); await app.listen(); } bootstrap();
1.3 更新子服务Controller的过滤器引用
将Controller上的@UseFilters(HttpExceptionFilter)替换为新的RPC过滤器:
// src/app.controller.ts import { MessagePattern, Payload, UseFilters } from '@nestjs/common'; import { RpcHttpExceptionFilter } from './exception-filter'; @MessagePattern('user') @UseFilters(RpcHttpExceptionFilter) async create(@Payload() createUserDto: CreateUserDto): Promise<usermain> { return await this.usersService.createUser(createUserDto); }
步骤2:修复主服务的异常捕获逻辑
2.1 正确处理RPC调用的Observable转Promise
NestJS的client.send()返回的是Observable,需要转换为Promise才能在async函数中正确处理。使用lastValueFrom(RxJS 7+)转换,并在catch中处理子服务传递的异常:
// src/app.service.ts import { Injectable } from '@nestjs/common'; import { ClientProxyFactory, Transport } from '@nestjs/microservices'; import { lastValueFrom } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { HttpException, HttpStatus } from '@nestjs/common'; @Injectable() export class AppService { public readonly client: ClientProxy; constructor() { this.client = ClientProxyFactory.create({ transport: Transport.TCP, options: { port: 5001, }, }); } async createNewUser(data: any): Promise<any> { const request$ = this.client.send<any>('user', data).pipe( catchError((error) => { // 捕获子服务传递的RpcException内容,转换为主服务的HTTP异常 throw new HttpException( error.response?.details || error.response?.message || '微服务调用失败', error.response?.statusCode || HttpStatus.BAD_REQUEST ); }) ); // 将Observable转换为Promise return await lastValueFrom(request$); } }
2.2 主服务全局异常处理(可选)
如果需要统一处理所有异常,可以在主服务的main.ts中注册全局HTTP异常过滤器,确保所有异常都能返回标准化响应:
// src/main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { ValidationPipe } from '@nestjs/common'; import { ClassSerializerInterceptor } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { HttpExceptionFilter } from './http-exception.filter'; // 自定义HTTP过滤器 async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe()); app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector))); app.useGlobalFilters(new HttpExceptionFilter()); // 注册全局HTTP异常过滤器 await app.listen(5000); } bootstrap();
关键说明
- 微服务间的RPC通信必须使用
RpcException传递异常,HTTP异常需要转换后才能被主服务正确解析。 - 主服务必须将Observable转换为Promise,否则async函数无法正确捕获异步异常。
- 子服务的ValidationPipe必须配置
exceptionFactory,确保校验错误能被转换为可序列化的异常对象。
内容的提问来源于stack exchange,提问作者Milan jotva
相关产品推荐
相关产品推荐

