grpc-ecosystem/grpc-gateway

GitHub: grpc-ecosystem/grpc-gateway

gRPC-Gateway 是一个 protoc 插件,能够根据 protobuf 服务定义自动生成反向代理,将 RESTful HTTP/JSON 请求转换为 gRPC 调用,实现一次定义、双协议服务。

Stars: 19836 | Forks: 2378

gRPC-Gateway

gRPC to JSON proxy generator following the gRPC HTTP spec

## 关于 gRPC-Gateway 是 Google protocol buffers 编译器 [protoc](https://github.com/protocolbuffers/protobuf) 的一个插件。 它读取 protobuf 服务定义并生成一个反向代理服务器,该服务器将 RESTful HTTP API 转换为 gRPC。该服务器是根据您服务定义中的 [`google.api.http`](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto#L46) 注解生成的。 这有助于您同时以 gRPC 和 RESTful 风格提供 API。
## 文档 您可以在以下地址阅读我们的文档: - https://grpc-ecosystem.github.io/grpc-gateway/ ## 评价 ## 背景 gRPC 非常出色——它可以在许多编程语言中生成 API 客户端和服务器存根,速度快、易于使用、带宽效率高,且其设计经过 Google 的实战检验。然而,您可能仍然希望同时提供传统的 RESTful JSON API。原因可能包括保持向后兼容性、支持 gRPC 支持不佳的语言或客户端,或者仅仅是为了维持 RESTful JSON 架构所涉及的美学性和工具链。 本项目旨在为您的 gRPC 服务提供 HTTP+JSON 接口。只需在您的服务中进行少量配置以附加 HTTP 语义,即可使用此库生成反向代理。 ## 安装 ### 从源码编译 以下说明假设您使用 [Go Modules](https://go.dev/wiki/Modules) 进行依赖管理。使用 [tool dependency](https://go.dev/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module) 来跟踪以下可执行包的版本: ``` // +build tools package tools import ( _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway" _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2" _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" _ "google.golang.org/protobuf/cmd/protoc-gen-go" ) ``` 运行 `go mod tidy` 来解析版本。通过运行以下命令进行安装: ``` go install \ github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \ github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \ google.golang.org/protobuf/cmd/protoc-gen-go \ google.golang.org/grpc/cmd/protoc-gen-go-grpc ``` 这将在您的 `$GOBIN` 中放置四个二进制文件: - `protoc-gen-grpc-gateway` - `protoc-gen-openapiv2` - `protoc-gen-go` - `protoc-gen-go-grpc` 请确保您的 `$GOBIN` 位于您的 `$PATH` 中。 ### **在 Go 1.24 中使用 `tool` 指令** 从 Go 1.24 开始,`go.mod` 中的 `tool` 指令提供了一种结构化的方式来跟踪和管理可执行依赖。这取代了之前使用带有空白导入的单独 `tools.go` 文件的变通方法。 #### **在 `go.mod` 中跟踪工具** 无需在 Go 源文件中手动导入工具依赖,您现在可以在 `go.mod` 中使用 `tool` 指令来声明项目所依赖的工具。例如: ``` module tools go 1.24 tool ( github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 google.golang.org/grpc/cmd/protoc-gen-go-grpc google.golang.org/protobuf/cmd/protoc-gen-go ) ``` #### **管理工具依赖** 要向您的模块添加工具,请使用 `go get` 的 `-tool` 标志: ``` go get -tool github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway go get -tool github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 go get -tool google.golang.org/protobuf/cmd/protoc-gen-go go get -tool google.golang.org/grpc/cmd/protoc-gen-go-grpc ``` 这将自动更新 `go.mod`,在 `tool` 指令下添加工具,并附带 `require` 语句以确保版本跟踪。 ### 安装工具 一旦工具依赖被正确记录在 `go.mod` 文件中,只需在项目的根目录下执行以下命令: ``` go install tool ``` 这将在您的 `$GOBIN` 中放置四个二进制文件: - `protoc-gen-grpc-gateway` - `protoc-gen-openapiv2` - `protoc-gen-go` - `protoc-gen-go-grpc` 请确保您的 `$GOBIN` 位于您的 `$PATH` 中。 ### 下载二进制文件 您也可以选择从 [GitHub releases 页面](https://github.com/grpc-ecosystem/grpc-gateway/releases/latest) 下载二进制文件。 我们在发布过程中使用 OpenSSF 的 [slsa-framework/slsa-github-generator](https://github.com/slsa-framework/slsa-github-generator) 生成 [SLSA3 签名](slsa.dev)。要验证发布的二进制文件: 1. 从 [slsa-framework/slsa-verifier#installation](https://github.com/slsa-framework/slsa-verifier#installation) 安装验证工具。 2. 从 [GitHub releases 页面](https://github.com/grpc-ecosystem/grpc-gateway/releases/latest) 下载来源文件 `attestation.intoto.jsonl`。 3. 运行验证器: ``` slsa-verifier -artifact-path -provenance attestation.intoto.jsonl -source github.com/grpc-ecosystem/grpc-gateway -tag ``` 或者,参阅下面关于远程管理插件版本的部分。 ## 用法 ### 1. 使用 protocol buffers 定义您的 [gRPC](https://grpc.io/docs/) 服务 `your_service.proto`: ``` syntax = "proto3"; package your.service.v1; option go_package = "github.com/yourorg/yourprotos/gen/go/your/service/v1"; message StringMessage { string value = 1; } service YourService { rpc Echo(StringMessage) returns (StringMessage) {} } ``` ### 2. 生成 gRPC stubs 此步骤生成 gRPC stubs,您可以使用它们来实现服务并在客户端进行调用: 这是一个示例 `buf.gen.yaml`,您可以使用 [buf](https://github.com/bufbuild/buf) 来生成 stubs: ``` version: v2 plugins: - local: protoc-gen-go out: gen/go opt: - paths=source_relative - local: protoc-gen-go-grpc out: gen/go opt: - paths=source_relative ``` 有了这个文件,您就可以使用 `buf generate` 生成文件了。 如果您使用 `protoc` 生成 stubs,以下是一个命令示例: ``` protoc -I . \ --go_out ./gen/go/ --go_opt paths=source_relative \ --go-grpc_out ./gen/go/ --go-grpc_opt paths=source_relative \ your/service/v1/your_service.proto ``` ### 3. 照常在 gRPC 中实现您的服务。 ### 4. 使用 `protoc-gen-grpc-gateway` 生成反向代理 此时,您有 3 个选择: - 不做进一步修改,使用默认映射到 HTTP 语义(方法、路径等)。 - 这适用于任何 `.proto` 文件,但不允许设置 HTTP 路径、请求参数或类似内容。 - 额外的 `.proto` 修改以使用自定义映射。 - 依赖于 `.proto` 文件中的参数来设置自定义 HTTP 映射。 - 不修改 `.proto`,而是使用外部配置文件。 - 依赖于外部配置文件来设置自定义 HTTP 映射。 - 主要用于源 proto 文件不受您控制的情况。 #### 1. 使用默认映射 这不需要对 `.proto` 文件进行额外修改,但在执行插件时需要启用特定选项。 应启用 `generate_unbound_methods`。 以下是启用此选项后的 `buf.gen.yaml` 文件示例: ``` version: v2 plugins: - local: protoc-gen-go out: gen/go opt: - paths=source_relative - local: protoc-gen-go-grpc out: gen/go opt: - paths=source_relative - local: protoc-gen-grpc-gateway out: gen/go opt: - paths=source_relative - generate_unbound_methods=true ``` 使用 `protoc`(仅 grpc-gateway stubs): ``` protoc -I . --grpc-gateway_out ./gen/go \ --grpc-gateway_opt paths=source_relative \ --grpc-gateway_opt generate_unbound_methods=true \ your/service/v1/your_service.proto ``` #### 2. 使用自定义注解 向您的 .proto 文件添加 [`google.api.http`](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto#L46) 注解 `your_service.proto`: ``` syntax = "proto3"; package your.service.v1; option go_package = "github.com/yourorg/yourprotos/gen/go/your/service/v1"; + +import "google/api/annotations.proto"; + message StringMessage { string value = 1; } service YourService { - rpc Echo(StringMessage) returns (StringMessage) {} + rpc Echo(StringMessage) returns (StringMessage) { + option (google.api.http) = { + post: "/v1/example/echo" + body: "*" + }; + } } ``` 有关可以添加以自定义网关行为和生成的 OpenAPI 输出的更多注解示例,请参阅 [a_bit_of_everything.proto](examples/internal/proto/examplepb/a_bit_of_everything.proto)。 这是一个示例 `buf.gen.yaml` 文件: ``` version: v2 plugins: - local: protoc-gen-go out: gen/go opt: - paths=source_relative - local: protoc-gen-go-grpc out: gen/go opt: - paths=source_relative - local: protoc-gen-grpc-gateway out: gen/go opt: - paths=source_relative ``` 如果您使用 `protoc` 生成 stubs,您需要确保编译器在编译时可以使用所需的依赖项。可以通过从 [googleapis repository](https://github.com/googleapis/googleapis) 手动克隆并复制相关文件,并在运行时将它们提供给 `protoc` 来找到这些文件。您需要的文件是: ``` google/api/annotations.proto google/api/field_behavior.proto google/api/http.proto google/api/httpbody.proto ``` 以下是一个 `protoc` 执行示例: ``` protoc -I . --grpc-gateway_out ./gen/go \ --grpc-gateway_opt paths=source_relative \ your/service/v1/your_service.proto ``` #### 3. 外部配置 如果您不想(或不能)修改 proto 文件以配合 gRPC-Gateway 使用,您可以选择使用外部 [gRPC Service Configuration](https://cloud.google.com/endpoints/docs/grpc/grpc-service-config) 文件。 有关更多信息,请参阅[我们的文档](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/grpc_api_configuration/)。这最好与 `standalone=true` 选项结合使用,以生成一个可以位于其自己的包中的文件,与源 protobuf 文件生成的文件分开。 以下是启用此选项后的 `buf.gen.yaml` 文件示例: ``` version: v2 plugins: - local: protoc-gen-go out: gen/go opt: - paths=source_relative - local: protoc-gen-go-grpc out: gen/go opt: - paths=source_relative - local: protoc-gen-grpc-gateway out: gen/go opt: - paths=source_relative - grpc_api_configuration=path/to/config.yaml - standalone=true ``` 使用 `protoc`(仅 grpc-gateway stubs): ``` protoc -I . --grpc-gateway_out ./gen/go \ --grpc-gateway_opt paths=source_relative \ --grpc-gateway_opt grpc_api_configuration=path/to/config.yaml \ --grpc-gateway_opt standalone=true \ your/service/v1/your_service.proto ``` ### 5. 为 HTTP 反向代理服务器编写入口点 ``` package main import ( "context" "flag" "net/http" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/grpclog" gw "github.com/yourorg/yourrepo/proto/gen/go/your/service/v1/your_service" // Update ) var ( // command-line options: // gRPC server endpoint grpcServerEndpoint = flag.String("grpc-server-endpoint", "localhost:9090", "gRPC server endpoint") ) func run() error { ctx := context.Background() ctx, cancel := context.WithCancel(ctx) defer cancel() // Register gRPC server endpoint // Note: Make sure the gRPC server is running properly and accessible mux := runtime.NewServeMux() opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} err := gw.RegisterYourServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts) if err != nil { return err } // Start HTTP server (and proxy calls to gRPC server endpoint) return http.ListenAndServe(":8081", mux) } func main() { flag.Parse() if err := run(); err != nil { grpclog.Fatal(err) } } ``` ### 6. (可选)使用 `protoc-gen-openapiv2` 生成 OpenAPI 定义 这是一个示例 `buf.gen.yaml` 文件: ``` version: v2 plugins: - local: protoc-gen-go out: gen/go opt: - paths=source_relative - local: protoc-gen-go-grpc out: gen/go opt: - paths=source_relative - local: protoc-gen-grpc-gateway out: gen/go opt: - paths=source_relative - generate_unbound_methods=true - local: protoc-gen-openapiv2 out: gen/go ``` 要使用 `protoc-gen-openapiv2` 支持的自定义 protobuf 注解,我们需要在 protobuf 生成步骤中添加另一个依赖项。如果您使用 `buf`,可以将 `buf.build/grpc-ecosystem/grpc-gateway` 依赖项添加到您的 `deps` 数组中: ``` version: v2 name: buf.build/yourorg/myprotos deps: - buf.build/googleapis/googleapis - buf.build/grpc-ecosystem/grpc-gateway ``` 使用 `protoc`(仅 swagger 文件): ``` protoc -I . --openapiv2_out ./gen/openapiv2 \ your/service/v1/your_service.proto ``` 如果您使用 `protoc` 生成 stubs,您需要从本仓库的 `protoc-gen-openapiv2/options` 目录复制 protobuf 文件,并在运行时将它们提供给 `protoc`。 请注意,此插件也支持为未注解的方法生成 OpenAPI 定义;使用 `generate_unbound_methods` 选项启用此功能。 在使用 HTTP 映射时,gRPC 服务方法可能会创建重复的映射,其唯一区别在于路径参数的约束,这是可能的。 `/v1/{name=projects/*}` 和 `/v1/{name=organizations/*}` 都变为 `/v1/{name}`。当发生这种情况时,插件将使用“_1”(或“_2”等)后缀重命名路径参数,以区分不同的操作。因此,在上面的示例中,第二个路径将变为 `/v1/{name_1=organizations/*}`。这也可能导致 OpenAPI 客户端对作为路径参数一部分的“/”进行 URL 编码,因为这是 OpenAPI 在规范中定义的。要允许 gRPC gateway 接受 URL 编码的斜杠并仍然路由请求,请使用 UnescapingModeAllCharacters 或 UnescapingModeLegacy(目前这是默认值,尽管在未来的版本中可能会更改)。有关更多信息,请参阅 [Customizing Your Gateway](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/customizing_your_gateway/)。 ## 使用远程插件 作为上述所有方法的替代方案,您可以使用 `buf` 和 [remote plugins](https://buf.build/docs/bsr/remote-plugins/usage) 来管理插件版本和生成。一个使用远程插件生成的示例 `buf.gen.yaml` 如下所示: ``` version: v2 plugins: - remote: buf.build/protocolbuffers/go:v1.31.0 out: gen/go opt: - paths=source_relative - remote: buf.build/grpc/go:v1.3.0 out: gen/go opt: - paths=source_relative - remote: buf.build/grpc-ecosystem/gateway:v2.16.2 out: gen/go opt: - paths=source_relative - remote: buf.build/grpc-ecosystem/openapiv2:v2.16.2 out: gen/openapiv2 ``` 这不需要在本地安装任何插件。请注意使用与运行时库相同版本的生成器,即如果使用 `v2.16.2`,请运行 ``` $ go get github.com/grpc-ecosystem/grpc-gateway/v2@v2.16.2 ``` 以便在您的 `go.mod` 中获得相同版本的运行时。 请注意,使用远程插件与使用外部配置文件(如 [grpc_api_configuration](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/grpc_api_configuration/#using-an-external-configuration-file))不兼容。 ## 视频介绍 来自我们的维护者 [@JohanBrandhorst](https://github.com/johanbrandhorst) 的 GopherCon UK 2019 演示很好地介绍了 gRPC-Gateway 的使用。它使用以下样板仓库作为基础:https://github.com/johanbrandhorst/grpc-gateway-boilerplate。 ## 参数和标志 当使用 `buf` 生成 stubs 时,标志和参数通过 `buf.gen.yaml` 文件中的 `opt` 字段传递,例如: ``` version: v2 plugins: - local: protoc-gen-grpc-gateway out: gen/go opt: - paths=source_relative - grpc_api_configuration=path/to/config.yaml - standalone=true ``` 在使用 `protoc` 生成代码期间,必须通过 `protoc` 使用以下两种模式之一将标志传递给 gRPC-Gateway 工具: - 作为 `--_out` `protoc` 参数的一部分:`--_out=:` ``` --grpc-gateway_out=repeated_path_param_separator=ssv:. --openapiv2_out=repeated_path_param_separator=ssv:. ``` - 使用额外的 `--_opt` 参数:`--_opt=[,]*` ``` --grpc-gateway_opt repeated_path_param_separator=ssv --openapiv2_opt repeated_path_param_separator=ssv ``` ## 更多示例 `examples` 目录下提供了更多示例。 - `proto/examplepb/echo_service.proto`, `proto/examplepb/a_bit_of_everything.proto`, `proto/examplepb/unannotated_echo_service.proto`: 服务定义 - `proto/examplepb/echo_service.pb.go`, `proto/examplepb/a_bit_of_everything.pb.go`, `proto/examplepb/unannotated_echo_service.pb.go`: [生成的] 服务 stub - `proto/examplepb/echo_service.pb.gw.go`, `proto/examplepb/a_bit_of_everything.pb.gw.go`, `proto/examplepb/uannotated_echo_service.pb.gw.go`: [生成的] 服务的反向代理 - `proto/examplepb/unannotated_echo_service.yaml`: `unannotated_echo_service.proto` 的 gRPC API Configuration - `server/main.go`: 服务实现 - `main.go`: 生成的反向代理的入口点 要对自定义 HTTP 处理程序(例如提供 `swagger.json`)、gRPC-Gateway 和 gRPC 服务器使用相同的端口,请参阅 [this example by CoreOS](https://github.com/philips/grpc-gateway-example/blob/master/cmd/serve.go)(及其随附的 [blog post](https://web.archive.org/web/20201112010739/https://coreos.com/blog/grpc-protobufs-swagger.html))。 [This example by neiro.ai](https://github.com/mynalabsai/grpc_gateway_media_example)(及其随附的 [blog post](https://medium.com/neiro-ai/grpc-gateway-for-media-api-by-neiro-9033caab12c8))展示了如何使用中间件将使用 `multipart/form-data` 的媒体文件集成到 rpc 消息中。 ## 特性 ### 支持 - 生成 JSON API 处理程序。 - 请求体中的方法参数。 - 请求路径中的方法参数。 - 查询字符串中的方法参数。 - 路径参数中的枚举字段(包括重复的枚举字段)。 - 将流式 API 映射到换行符分隔的 JSON 流。 - 将带有 `Grpc-Metadata-` 前缀的 HTTP 标头映射到 gRPC 元数据(带有 `grpcgateway-` 前缀) - 可选地为 [OpenAPI (Swagger) v2](https://swagger.io/docs/specification/2-0/basic-structure/) 发出 API 定义。 - 通过入站 HTTP `Grpc-Timeout` 标头设置 [gRPC timeouts](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests)。 - 部分支持 [gRPC API Configuration](https://cloud.google.com/endpoints/docs/grpc/grpc-service-config) 文件作为注解的替代方案。 - 自动将 PATCH 请求转换为 Field Mask gRPC 请求。有关更多信息,请参阅 [the docs](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/patch_feature/)。 - [Protobuf Editions](https://protobuf.dev/editions/overview/) 支持(edition 2023)。 - Go [Opaque API](https://go.dev/blog/protobuf-opaque) 支持。 ### 不计划支持 但欢迎提交补丁。 - HTTP 标头中的方法参数。 - 处理 trailer 元数据。 - 以 XML 编码请求/响应体。 - 真正的双向流。 ## 将 gRPC 映射到 HTTP - [gRPC 错误代码如何在响应中映射到 HTTP状态代码](https://github.com/grpc-ecosystem/grpc-gateway/blob/main/runtime/errors.go#L15)。 - HTTP 请求源 IP 作为 `X-Forwarded-For` gRPC 请求标头添加。 - HTTP 请求主机作为 `X-Forwarded-Host` gRPC 请求标头添加。 - HTTP `Authorization` 标头作为 `authorization` gRPC 请求标头添加。 - 其余永久性 HTTP 标头键(如 IANA [此处](http://www.iana.org/assignments/message-headers/message-headers.xhtml) 所指定)以 `grpcgateway-` 为前缀,并将其值添加到 gRPC 请求标头中。 - 以 'Grpc-Metadata-' 开头的 HTTP 标头映射到 gRPC 元数据(带有 `grpcgateway-` 前缀)。 - 虽然可配置,但默认的 {解,}编组使用 [protojson](https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson)。 - 用于将 gRPC 服务方法映射到 HTTP 端点的路径模板支持 [google.api.http](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto) 路径模板语法。例如,`/api/v1/{name=projects/*/topics/*}` 或 `/prefix/{path=organizations/**}`。 ## 贡献 请参阅 [CONTRIBUTING.md](http://github.com/grpc-ecosystem/grpc-gateway/blob/main/CONTRIBUTING.md)。 ## 许可证 gRPC-Gateway 根据 BSD 3-Clause 许可证授权。 有关更多详细信息,请参阅 [LICENSE](https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE)。
标签:API 网关, DNS解析, Go, Google API HTTP, gRPC, HTTP 转换, JSON, Protocol Buffers, Protoc 插件, Python工具, RESTful API, Ruby工具, 代理生成器, 代码生成, 反向代理, 后端开发, 开源项目, 接口转换, 提示词优化, 日志审计, 渗透测试工具, 通信协议