如何从 protobuf (.proto) 文件中生成 (.json/.yaml) 中的 swagger3 (OpenAPI3) 规范?

Posted

技术标签:

【中文标题】如何从 protobuf (.proto) 文件中生成 (.json/.yaml) 中的 swagger3 (OpenAPI3) 规范?【英文标题】:How to generate swagger3 (OpenAPI3) spec in (.json/.yaml) from protobuf (.proto) files? 【发布时间】:2020-04-21 18:03:08 【问题描述】:

我原来的用例:

我正在使用gRPC 服务器(使用protobuf)在GO 中构建一个应用程序,并将其包装在HTTPS 服务器中(使用gin)。只有 HTTPS 服务器被发布到客户端以供使用(我的意思是我的应用程序可以通过 REST API 访问,然后实际上在 gRPC 端点上拨号),我使用Swagger OpenAPI3(版本 3)发布它是这里的主要要求)规范。 gRPC 和 HTTPS 都是必需的,任何解决方案都应遵循此架构。

我不想在两个地方维护我的服务器规范,即我不想同时维护 proto 文件 (.proto) 和 swagger 规范 (.json/.yaml)。由于我必须编写 proto 文件来生成 gRPC 服务器,因此我想自动生成 swagger 规范 (OpenAPI3)。

我在哪里:

我可以使用 grpc-gateway 库从 protobuf 文件 (.proto) 生成 swagger OpenAPI2 规范,类似于:grpc-rest-go-example。但我的要求是 OpenAPI3;更具体地说,我想在 OpenAPI3 中使用 oneOf 功能并从 proto 的 oneof 功能映射到它。 这在 OpenAPI2 中是不可能的,因为它不允许 API 具有多个类型定义的请求/响应主体,这是 OpenAPI3 通过启用 oneOf、anyOf 和 allOf 构造添加的功能。

在尝试这样做时,我偶然发现了 GoogleAPIs googleapis/gnostic 提供的这个库,其描述是:

此存储库包含一个 Go 命令行工具,可将 JSON 和 YAML OpenAPI 描述与等效的协议缓冲区表示形式相互转换。

乍一看,这似乎完全解决了我的问题,但事实证明,这个库只在协议缓冲区 (protobuf) 二进制 (.pb) 和 swagger OpenAPI2/OpenAPI3 (.json/.yaml) 文件之间相互转换,这给我带来了我的新问题。

例如对于以下.pb 文件:


�3.0.1�…�
�Example service��Example service description*�
�Example contact2=

Apache 2.0�/http://www.apache.org/licenses/LICENSE-2.0.html:�1.0�!
�//localhost:9999/example/api/v1"â�
�
�/exampleResource��"���Example API��Example API description*�example-operation2B
@
example-query��query��example-query description �R�
    Ê��stringBÇ��œ�
�200�”�
‘�
�OK�Š�
C
�application/json�/
-�+
)#/components/schemas/common.StatusMessage
C
�application/yaml�/
-�+
)#/components/schemas/common.StatusMessage�¥�
�400���
š�
�Bad Request�Š�
C
�application/json�/
-�+
)#/components/schemas/common.StatusMessage
C
�application/yaml�/
-�+
)#/components/schemas/common.StatusMessage*Y
W
U
�common.StatusMessage�=
;Ê��objectú�/
�
�message��
    ��string
�
�status��
    ��string

它会生成以下招摇文件:

openapi: 3.0.1
info:
  title: Example service
  description: Example service description
  contact:
    name: Example contact
  license:
    name: Apache 2.0
    url: http://www.apache.org/licenses/LICENSE-2.0.html
  version: "1.0"
servers:
- url: //localhost:9999/example/api/v1
paths:
  /exampleResource:
    get:
      summary: Example API
      description: Example API description
      operationId: example-operation
      parameters:
      - name: example-query
        in: query
        description: example-query description
        required: true
        schema:
          type: string
      responses:
        200:
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/common.StatusMessage'
            application/yaml:
              schema:
                $ref: '#/components/schemas/common.StatusMessage'
        400:
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/common.StatusMessage'
            application/yaml:
              schema:
                $ref: '#/components/schemas/common.StatusMessage'
components:
  schemas:
    common.StatusMessage:
      type: object
      properties:
        message:
          type: string
        status:
          type: string

.pb 可能无法正常查看,请访问它here。所以像:

�status��
    ��string

看起来像:

<0x06>status<0x12><0x0b>
    Ê<0x01><0x06>string

对于上面的例子,我首先编写了 swagger 规范,然后生成了 .pb,但同样可以反过来做。

当前状态:

如果我有办法在 (.pb) 和 (.proto) 文件之间进行转换,则转换循环将关闭并完成 (.proto -> .pb -> .json/.yaml -> .pb -> .proto)。

我确信必须有一种方法来实现这一点,因此我的原始问题存在解决方案。但是我找不到任何可以做到这一点的文章或代码。 .pb.proto 文件之间是否有合理的方法来相互转换?

如果您对我的原始用例有完全不同的解决方案,也请随时分享。这会很有帮助。

提前致谢!

编辑:

(1) 感谢最近的 cmets,很明显 .pb.proto 之间的“转换”首先是一个荒谬的问题。但最初的问题仍然相同,即如何使用注释、标签或其他方式从 protobuf 文件 (.proto) 生成 swagger3 (OpenAPI3) 规范。 相应地更改问题标题。

(2) 就在我发布这个后的第二天,我碰到了gnostic-grpc 存储库,其中的描述是:

此工具将 OpenAPI v3.0 API 描述转换为 gRPC 服务的描述,该服务可用于使用 gRPC-JSON 转码实现该 API。

同样,这让我过早退出。实际上,这是一个 GSOC 项目,尽管这个存储库的想法令人惊叹,但它并没有满足 requirements 的要求。此外,这不是一个相互转换的库,对于任何生产用途都非常不成熟。事实上,它未能提供 OpenAPI3 规范的一些基本特性。

但是这个存储库正朝着正确的方向前进。我的结论是有一个自定义插件可以做到这一点,主要是通过扩展 GO 中的注释库。

(3) 显然,从 .proto 转换为 OpenAPI3 规范 (.yaml/.json) 没有好的和明显的候选对象,除了 gnostic-grpc,它非常不成熟,并且正在为任何类型的实际用途进行大量工作。

但是对于反向转换,即OpenAPI3规范(.yaml/.json)到.proto,在OpenAPITools下有一个很好的库叫做openapi-generator,它可以将OpenAPI v2/3规范转换为几乎所有平台的客户端/服务器存根.但由于这不是最初的问题,所以问题仍然悬而未决。

【问题讨论】:

您根本不会将 protobuf 有效负载 转换为 .proto;这并不意味着什么。有 2 种 protobuf 格式:(a) 二进制 protobuf 编码 - 这就是您在问题中所称的 .pb,以及 (b) 在 json 中表达相同意图的固执己见的 json 变体;在这种情况下(即 .proto 意味着完全不同的东西),你能澄清你想在这里做什么吗? 哦,好吧!我的确切用例是编写 proto 文件 -> 生成 gRPC 服务器 -> 生成 HTTPS 服务器并为服务器生成 swagger 规范。我知道这对于使用 grpc-gateway 库(在 .proto 文件中使用注释)的 openAPI2 是可能的,但是对于 OpenAPI3 找不到任何东西。我想使用注释、标签或其他方式将 .proto 转换为 OpenAPI3 规范。 grpc-gateway 有一个问题开放了几年来支持 openAPI v3:github.com/grpc-ecosystem/grpc-gateway/issues/441 我也需要这个。也许我们可以一起努力。由于它已经生成了 openAPIv2,因此它的提升似乎比从头开始编写要少。 我很乐意为此工作。目前我正在开发 python 脚本来进行这种相互转换,我将开源它,但是在更容易访问的存储库中更通用的解决方案将更加令人惊奇,无论如何,python 脚本只是一种解决方法。但是关于第二部分,我认为它肯定会比从头开始写东西要少,但还是很多,这很好!这是因为 OpenAPIv3 是高度向后不兼容的。 【参考方案1】:

虽然,您的要求似乎是从 PROTO 获取 YAML (OpenAPNv3) 规范,但您可以查看此插件 - gnostic-grpc - 用于 gnostic,它执行相反的操作,即从 YAML/JSON 规范转换为原型使用 gRPC 服务调用。

【讨论】:

是的,就在我发布这个问题的那天之后,我遇到了这个问题。尽管这个存储库的想法很神奇,但它还没有准备好使用。这是一个 GSOC 项目,有错误。正如你所说,它的转换是一种方式,而且是不完整的。它不支持大多数 openAPI 结构,并且它支持的结构没有利用 openAPI 3 功能。我得出的结论是,编写插件是最好的解决方案,这并不难。最重要的是,我决定为诺斯替教做出贡献,因为我喜欢这个想法。这是一个真实的用例,令人惊讶的是没有解决方案。 绝对如此。事实上,由于对 OpenAPI 的看似不完整或部分支持,我们一直在尝试使用它并陷入多重限制。但令人惊讶的是,不幸的是,除了这个之外,没有其他实现能像从 YAML 中获取 proto(特别是 gRPC)那样接近。 我在 GitHub 上也有一个 open question 。 @AnupamaDeshmukh 我是 gnostic-grpc 的主要贡献者之一。您能否更详细地说明什么对您不起作用?随意在存储库中打开问题。

以上是关于如何从 protobuf (.proto) 文件中生成 (.json/.yaml) 中的 swagger3 (OpenAPI3) 规范?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Protocol Buffers .NET 代码生成 .proto 文件?

从 protobuf-net 中的自定义 RuntimeTypeModel 生成 .proto 文件

如何从 proto 文件中使用 ProtoGen 生成结构

.proto 文件中带有 oneof 的 Protobuf-net

从不同的项目.Net访问protobuf

protobuf-net - 反引号、字典和 .proto 文件