grpc-ecosystem/grpc-gateway
GitHub: grpc-ecosystem/grpc-gateway
gRPC-Gateway 是一个 protoc 插件,能够根据 protobuf 服务定义自动生成反向代理,将 RESTful HTTP/JSON 请求转换为 gRPC 调用,实现一次定义、双协议服务。
Stars: 19836 | Forks: 2378
## 关于
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工具, 代理生成器, 代码生成, 反向代理, 后端开发, 开源项目, 接口转换, 提示词优化, 日志审计, 渗透测试工具, 通信协议