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

@grpc/grpc-js能否动态解析google.protobuf.Any类型?

动态解析gRPC中google.protobuf.Any类型的错误详情

我正在用@grpc/grpc-js开发gRPC客户端,需要处理服务器流式Init方法返回的错误:返回的InitResponse里包含google.rpc.Status对象,其details数组的元素是google.protobuf.Any类型,我需要解析这些应用特定的错误,但无法预先知晓错误的具体类型。

相关Proto定义

Init方法定义:

// Initializes an existing Arduino Core instance by loading platforms and
// libraries
rpc Init(InitRequest) returns (stream InitResponse) {}

InitResponse定义:

message InitResponse {
  message Progress {
    // Progress of the downloads of platforms and libraries index files.
    DownloadProgress download_progress = 1;
    // Describes the current stage of the initialization.
    TaskProgress task_progress = 2;
  }
  oneof message {
    Progress init_progress = 1;
    google.rpc.Status error = 2;
    // Selected profile information
    Profile profile = 3;
  }
}

错误详情示例

details数组中的Any类型示例:

{
  "typeUrl": "type.googleapis.com/cc.arduino.cli.commands.v1.PlatformLoadingError",
  "value": ""
}
{
  "typeUrl": "type.googleapis.com/cc.arduino.cli.commands.v1.FailedInstanceInitError",
  "value": "CAIStAFMb2FkaW5nIGluZGV4IGZpbGU6IGxvYWRpbmcganNvbiBpbmRleCBmaWxlIC9Vc2Vycy9hLmtpdHRhL0xpYnJhcnkvQXJkdWlubzE1L3BhY2thZ2VfaW5kZXguanNvbjogb3BlbiAvVXNlcnMvYS5raXR0YS9MaWJyYXJ5L0FyZHVpbm8xNS9wYWNrYWdlX2luZGV4Lmpzb246IG5vIHN1Y2ggZmlsZSBvciBkaXJlY3Rvcnk="
}

已知类型的常规处理方式

如果预先知道错误类型,可以用Any#unpack方法解析,示例代码如下:

const error: Status | undefined = resp.getError();
if (error) {
  const details: Any[] = error.getDetailsList();
  if (details.length) {
    const any = details[0];
    const typeName = any.getTypeName();
    switch (typeName) {
      case 'cc.arduino.cli.commands.v1.FailedInstanceInitError': {
        const initError = any.unpack(
          FailedInstanceInitError.deserializeBinary,
          typeName
        );
        console.log('Handle the init error', initError);
        break;
      }
      case 'cc.arduino.cli.commands.v1.PlatformLoadingError': {
        const loadError = any.unpack(
          PlatformLoadingError.deserializeBinary,
          typeName
        );
        console.log('Handle the load error', loadError);
        break;
      }
      default: {
        console.log('Unexpected error');
      }
    }
  }
}

核心问题

能否通过typeName动态获取对应类型的反序列化方法?根据protocolbuffers/protobuf-javascript的讨论,这似乎没有官方支持,想了解业界的最佳实践是什么?


临时可行方案

我找到了一个临时解决方案:proto生成的JS模块中,deserializeBinary方法会挂载到全局对象的proto属性下,借助get-value包可以编写一个通用函数来获取该方法:

import { Message } from 'google-protobuf';
import get = require('get-value');

export function globalDeserializeBinary<T extends Message = Message>(
  typeName: string
): ((data: Uint8Array) => T) | undefined {
  return get(global, ['proto', typeName, 'deserializeBinary'].join('.'));
}

修改后的解析代码:

const error: Status | undefined = resp.getError();
if (error) {
  const details: Any[] = error.getDetailsList();
  if (details.length) {
    const any = details[0];
    const typeName = any.getTypeName();
    const deserialize = globalDeserializeBinary(typeName);
    if (deserialize) {
      const error = any.unpack(deserialize, typeName);
      if (error) {
        console.log('BLABLA', JSON.stringify(error.toObject(false)));
      }
    }
  }
}

运行后可以得到预期的错误对象:

BLABLA {"reason":2,"message":"Loading index file: loading json index file /Users/a.kitta/Library/Arduino15/package_index.json: open /Users/a.kitta/Library/Arduino15/package_index.json: no such file or directory"}
BLABLA {"reason":2,"message":"Loading index file: loading json index file /Users/a.kitta/Library/Arduino15/package_teensy_index.json: open /Users/a.kitta/Library/Arduino15/package_teensy_index.json: no such file or directory"}
BLABLA {}
BLABLA {}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 13:18:09