NestJS微服务中ValidationPipe异常转RpcException问题求助
解决微服务中ValidationPipe异常转RPC格式问题
问题根源
ValidationPipe默认抛出BadRequestException(HTTP类型异常),但在RabbitMQ微服务场景下,网关无法正确识别这类HTTP异常,会默认返回500状态码。我们需要将验证异常转换为RPC场景专用的RpcException,同时让网关能将RpcException映射为对应的HTTP响应。
解决方案步骤
1. 在业务服务(Auth服务)中配置ValidationPipe抛出RpcException
直接通过ValidationPipe的exceptionFactory自定义异常类型,替代默认的HTTP异常。修改Auth服务的main.ts:
import { ValidationPipe } from '@nestjs/common'; import { RpcException } from '@nestjs/microservices'; async function bootstrap() { const app = await NestFactory.create(AuthModule); // 全局配置ValidationPipe,将验证错误转为RpcException app.useGlobalPipes(new ValidationPipe({ whitelist: true, exceptionFactory: (validationErrors) => { // 格式化验证错误信息 const errorMessages = validationErrors.map(err => Object.values(err.constraints).join(', ') ).join('; '); // 返回带状态码和消息的RpcException return new RpcException({ statusCode: 400, message: errorMessages, }); }, })); const configService = app.get(ConfigService); const rmqService = app.get(RmqService); const queue = configService.get('RABBITMQ_AUTH_QUEUE'); app.connectMicroservice(rmqService.getOptions(queue)); await app.startAllMicroservices(); } bootstrap();
注意:此时可以删除
UserDto上的@UsePipes(new ValidationPipe({ whitelist: true })),因为全局已经配置了ValidationPipe,避免重复执行验证。
2. 在API网关中添加RpcException过滤器
网关需要捕获业务服务传来的RpcException,并转换为对应的HTTP响应状态码和消息。创建一个全局异常过滤器:
// src/common/filters/rpc-exception.filter.ts import { ExceptionFilter, Catch, ArgumentsHost, HttpStatus } from '@nestjs/common'; import { RpcException } from '@nestjs/microservices'; import { Response } from 'express'; @Catch(RpcException) export class RpcExceptionFilter implements ExceptionFilter { catch(exception: RpcException, host: ArgumentsHost) { const httpCtx = host.switchToHttp(); const response = httpCtx.getResponse<Response>(); const errorData = exception.getError(); // 根据异常数据结构返回对应HTTP响应 if (typeof errorData === 'object' && 'statusCode' in errorData && 'message' in errorData) { response.status(errorData.statusCode).json({ statusCode: errorData.statusCode, message: errorData.message, }); } else { // 处理非标准RpcException情况 response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ statusCode: HttpStatus.INTERNAL_SERVER_ERROR, message: errorData || '服务内部错误', }); } } }
然后在网关的main.ts中注册这个全局过滤器:
import { RpcExceptionFilter } from './common/filters/rpc-exception.filter'; async function bootstrap() { const app = await NestFactory.create(AppModule); // 注册RpcException全局过滤器 app.useGlobalFilters(new RpcExceptionFilter()); await app.listen(3000); } bootstrap();
3. 优化网关控制器参数(可选)
网关的ApiGateway控制器可以直接使用UserDto接收请求体,在网关层也做一次基础验证,提前拦截无效请求:
@Controller() export class ApiGateway { constructor( @Inject('AUTH_SERVICE') private authService: ClientProxy, ) {} @Post('auth/register') async register(@Body() newUser: UserDto) { return this.authService.send({ cmd: 'register' }, newUser); } }
此时需要在网关中也配置ValidationPipe(同样可以用全局配置),不过这一步是可选的,核心还是业务服务的异常转换。
效果验证
当请求参数不符合UserDto的验证规则时,Auth服务会抛出带400状态码和验证消息的RpcException,网关捕获后返回400 HTTP状态码和对应的错误信息,不再返回500。
内容的提问来源于stack exchange,提问作者wannnwdooo
相关产品推荐
相关产品推荐

