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

NestJS如何返回数组形式的gRPC校验异常而非拼接字符串

问题描述

底层使用 yup 对发送至应用的 gRPC 请求做校验,校验阶段可生成如下格式的错误数组:

message: [
  'filter.fieldOne must be one of the following values: 1, 2, 3',
  'filter.fieldTwo must not be one of the following values: 0'
]

当前使用自定义 BaseRpcExceptionFilter 实现全局异常处理,代码如下:

@Catch()
export class AllRpcExceptionsFilter extends BaseRpcExceptionFilter {
  constructor(private readonly newRelicService: NewRelicService) {
    super();
  }

  catch(exception: Error, host: ArgumentsHost) {
    this.newRelicService.noticeError(exception, {
      exception: JSON.stringify(exception),
    });

    return super.catch(exception, host);
  }
}

实际返回的错误响应中,原始错误数组被自动拼接为单字符串,响应格式如下:

{
  "error": "3 INVALID_ARGUMENT: filter.executionSystem must be one of the following values: 1, 2, 3,filter.executionSystem must not be one of the following values: 0"
}

需求是让异常响应返回数组形式的错误内容,而非拼接后的字符串对象。

实现方案

问题根源是Nest内置的BaseRpcExceptionFilter默认逻辑会对数组类型的message字段做逗号拼接处理,最终封装为带gRPC状态码前缀的单字符串返回,要保留数组格式的错误内容,只需要绕开这层默认拼接逻辑即可,可参考以下两种实现方式:

  • 方式一:改造异常抛出与过滤器返回逻辑
    抛出自定义RpcException时,不要把错误数组直接传入message字段,而是将原始错误数组存入异常对象的自定义details属性,同时在过滤器中放弃调用父类catch方法,手动组装返回结构:
    // yup校验失败位置的抛错逻辑
    import { status } from '@grpc/grpc-js';
    throw new RpcException({
      code: status.INVALID_ARGUMENT, // 对应gRPC无效参数状态码3
      message: '请求参数校验失败',
      details: yupValidationError.errors // 传入yup生成的原始错误数组
    })
    
    对应改造后的异常过滤器代码:
    import { BaseRpcExceptionFilter, RpcException } from '@nestjs/microservices';
    import { status } from '@grpc/grpc-js';
    
    @Catch()
    export class AllRpcExceptionsFilter extends BaseRpcExceptionFilter {
      constructor(private readonly newRelicService: NewRelicService) {
        super();
      }
    
      catch(exception: RpcException | Error, host: ArgumentsHost) {
        this.newRelicService.noticeError(exception, {
          exception: JSON.stringify(exception),
        });
    
        const errorContent = exception instanceof RpcException ? exception.getError() : exception;
        // 手动组装返回结构,保留数组格式的错误详情
        return {
          code: typeof errorContent === 'object' && 'code' in errorContent 
            ? errorContent.code 
            : status.INTERNAL,
          message: typeof errorContent === 'object' && 'message' in errorContent 
            ? errorContent.message 
            : exception.message,
          details: Array.isArray(errorContent?.['details']) 
            ? errorContent['details'] 
            : [exception.message]
        };
      }
    }
    
  • 方式二:不改动抛错逻辑,直接在过滤器中提取原始错误数组
    yup校验抛出的错误对象本身自带errors数组属性,在过滤器中捕获到异常后,直接判断异常来源是否为yup校验错误,提取原始errors数组后组装到返回结构中即可,同样不需要调用父类的默认catch方法。

注意:gRPC原生协议本身支持通过details字段传递结构化数据,只要服务端与客户端约定好错误字段的结构,就可以正常获取数组格式的错误内容,不会被强制序列化为字符串。

内容的提问来源于stack exchange,提问作者Przemek Wit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 02:48:32