You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS TCP微服务异常处理:主服务无法捕获子服务异常

解决NestJS TCP微服务主服务捕获子服务异常的问题

问题根源有两个核心点:

  1. 子服务使用了HTTP上下文的异常过滤器,但TCP微服务属于RPC通信,无法通过HTTP上下文处理异常,导致异常没有被正确序列化传递给主服务。
  2. 主服务对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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 08:47:44