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
相关产品推荐
相关产品推荐

