ASP.NET Core 中的 gRPC JSON 转码
注意
此版本不是本文的最新版本。 有关当前版本,请参阅本文的 .NET 9 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 对于当前版本,请参阅此文的 .NET 8 版本。
gRPC 是一种高性能远程过程调用 (RPC) 框架。 gRPC 使用 HTTP/2、流式传输、Protobuf 和消息协定来创建高性能的实时服务。
gRPC 有一个限制,即不是所有平台都可以使用它。 浏览器并不完全支持 HTTP/2,这使得 REST API 和 JSON 成为将数据引入浏览器应用的主要方式。 尽管 gRPC 带来了很多好处,但 REST API 和 JSON 在现代应用中仍占有重要地位。 构建 gRPC 和 JSON Web API 给应用开发增加了不必要的开销。
本文讨论如何使用 gRPC 服务创建 JSON Web API。
概述
gRPC JSON 转码是为 gRPC 服务创建 RESTful JSON API 的 ASP.NET Core 的扩展。 配置转码后,应用可以使用熟悉的 HTTP 概念调用 gRPC 服务:
- HTTP 谓词
- URL 参数绑定
- JSON 请求/响应
gRPC 仍然可以用来调用服务。
注意
gRPC ON 转码取代了 gRPC HTTP API,这是一个替代的实验性扩展。
使用情况
通过添加
AddJsonTranscoding
,在服务器启动代码中注册转码:在Program.cs
文件中,将builder.Services.AddGrpc();
更改为builder.Services.AddGrpc().AddJsonTranscoding();
。将
<IncludeHttpRuleProtos>true</IncludeHttpRuleProtos>
添加到项目文件 (.csproj
) 的属性组:<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <InvariantGlobalization>true</InvariantGlobalization> <IncludeHttpRuleProtos>true</IncludeHttpRuleProtos> </PropertyGroup>
用 HTTP 绑定和路由在
.proto
文件中注释 gRPC 方法:syntax = "proto3"; option csharp_namespace = "GrpcServiceTranscoding"; import "google/api/annotations.proto"; package greet; // The greeting service definition. service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) { option (google.api.http) = { get: "/v1/greeter/{name}" }; } } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings. message HelloReply { string message = 1; }
SayHello
gRPC 方法现在可以作为 gRPC 和 JSON Web API 调用:
- 请求:
GET /v1/greeter/world
- 响应:
{ "message": "Hello world" }
如果服务器配置为针对每个请求写入日志,则服务器日志显示 gRPC 服务执行 HTTP 调用。 转码将传入的 HTTP 请求映射到 gRPC 消息,然后将响应消息转换为 JSON。
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET https://localhost:5001/v1/greeter/world
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'gRPC - /v1/greeter/{name}'
info: Server.GreeterService[0]
Sending hello to world
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'gRPC - /v1/greeter/{name}'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 1.996ms 200 application/json
批注 gRPC 方法
gRPC 方法必须在支持转码之前使用 HTTP 规则进行注释。 HTTP 规则包括有关如何调用 gRPC 方法的信息,例如 HTTP 方法和路由。
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
get: "/v1/greeter/{name}"
};
}
}
继续浏览示例:
- 使用
SayHello
方法定义Greeter
服务。 该方法具有使用名称google.api.http
指定的 HTTP 规则。 - 可以使用
GET
请求和/v1/greeter/{name}
路由访问该方法。 - 请求消息上的
name
字段绑定到路由参数。
许多选项可用于自定义 gRPC 方法绑定到 RESTful API 的方式。 有关批注 gRPC 方法和自定义 JSON 的详细信息,请参阅为 gRPC JSON 转码配置 HTTP 和 JSON。
流式处理方法
HTTP/2 上的传统 gRPC 支持所有方向的流式处理。 转码仅限于服务器流式处理。 不支持客户端流式处理和双向流式处理方法。
服务器流式处理方法使用以行分隔的 JSON。 使用 WriteAsync
写入的每条消息都会序列化为 JSON,并在后面换一行。
以下服务器流式处理方法写入三条消息:
public override async Task StreamingFromServer(ExampleRequest request,
IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
for (var i = 1; i <= 3; i++)
{
await responseStream.WriteAsync(new ExampleResponse { Text = $"Message {i}" });
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
客户端接收三个以行分隔的 JSON 对象:
{"Text":"Message 1"}
{"Text":"Message 2"}
{"Text":"Message 3"}
请注意,WriteIndented
JSON 设置不适用于服务器流式处理方法。 整齐打印将新行和空格添加到 JSON,无法与以行分隔的 JSON 一起使用。
查看或下载 ASP.NET Core gPRC 转码和流式处理应用示例。
HTTP 协议
.NET SDK 中包含的 ASP.NET Core gRPC 服务模板创建仅针对 HTTP/2 配置的应用。 当应用仅支持传统的 gRPC over HTTP/2 时,HTTP/2 是很好的默认设置。 但是,转码同时适用于 HTTP/1.1 和 HTTP/2。 某些平台(如 UWP 或 Unity)无法使用 HTTP/2。 若要支持所有客户端应用,请将服务器配置为启用 HTTP/1.1 和 HTTP/2。
更新 appsettings.json
中的默认协议:
{
"Kestrel": {
"EndpointDefaults": {
"Protocols": "Http1AndHttp2"
}
}
}
在同一端口上启用 HTTP/1.1 和 HTTP/2 需要 TLS 进行协议协商。 有关在 gRPC 应用中配置 HTTP 协议的详细信息,请参阅 ASP.NET Core gRPC 协议协商。
gRPC JSON 转码与 gRPC-Web
转码和 gRPC-Web 都支持从浏览器调用 gRPC 服务。 但是,它们的操作方式是不同的:
- gRPC-Web 允许浏览器应用通过 gRPC-Web 客户端和 Protobuf 从浏览器调用 gRPC 服务。 gRPC-Web 需要浏览器应用生成 gRPC 客户端,并且具有快速发送小型 Protobuf 消息的优点。
- 转码允许浏览器应用调用 gRPC 服务,就像使用 JSON 调用 RESTful API 一样。 浏览器应用不需要生成 gRPC 客户端或了解 gRPC 的任何信息。
可以使用浏览器 JavaScript API 调用以前的 Greeter
服务:
var name = nameInput.value;
fetch('/v1/greeter/' + name)
.then((response) => response.json())
.then((result) => {
console.log(result.message);
// Hello world
});
grpc-gateway
Grpc-gateway 是从 grpc 服务创建 RESTful JSON API 的另一种技术。 它使用相同的 .proto
注释将 HTTP 概念映射到 gRPC 服务。
grpc-gateway 使用代码生成来创建反向代理服务器。 反向代理将 RESTful 调用转换为 gRPC+Protobuf,然后通过 HTTP/2 将调用发送到 gRPC 服务。 这种方法的优点是 gRPC 服务不知道 RESTful JSON API。 任何 gRPC 服务器都可以使用 grpc-gateway。
同时,gRPC JSON 转码在 ASP.NET Core 应用内运行。 它将 JSON 反序列化为 Protobuf 消息,然后直接调用 gRPC 服务。 ASP.NET Core 中的转码为 .NET 应用开发人员提供了以下优势:
- 复杂性更低:gRPC 服务和映射的 RESTful JSON API 都在同一个 ASP.NET Core 应用外部运行。
- 性能更佳:转码将 JSON 反序列化为 Protobuf 消息,并直接调用 gRPC 服务。 与对不同的服务器进行新的 gRPC 调用相比,执行此进程内操作时性能有显著的优势。
- 成本更低:减少了服务器数量,降低了每月托管费用。
如需了解 grpc-gateway 的安装和使用,请参阅 grpc-gateway 自述文件。
其他资源
gRPC 是一种高性能远程过程调用 (RPC) 框架。 gRPC 使用 HTTP/2、流式传输、Protobuf 和消息协定来创建高性能的实时服务。
gRPC 有一个限制,即不是所有平台都可以使用它。 浏览器并不完全支持 HTTP/2,这使得 REST API 和 JSON 成为将数据引入浏览器应用的主要方式。 尽管 gRPC 带来了很多好处,但 REST API 和 JSON 在现代应用中仍占有重要地位。 构建 gRPC 和 JSON Web API 给应用开发增加了不必要的开销。
本文讨论如何使用 gRPC 服务创建 JSON Web API。
概述
gRPC JSON 转码是为 gRPC 服务创建 RESTful JSON API 的 ASP.NET Core 的扩展。 配置转码后,应用可以使用熟悉的 HTTP 概念调用 gRPC 服务:
- HTTP 谓词
- URL 参数绑定
- JSON 请求/响应
gRPC 仍然可以用来调用服务。
注意
gRPC ON 转码取代了 gRPC HTTP API,这是一个替代的实验性扩展。
使用情况
- 将包引用添加到
Microsoft.AspNetCore.Grpc.JsonTranscoding
。 - 通过添加
AddJsonTranscoding
,在服务器启动代码中注册转码:在Program.cs
文件中,将builder.Services.AddGrpc();
更改为builder.Services.AddGrpc().AddJsonTranscoding();
。 - 在包含
.csproj
文件的项目目录中创建目录结构/google/api
。 - 将
google/api/http.proto
和google/api/annotations.proto
文件添加到/google/api
目录中。 - 用 HTTP 绑定和路由在
.proto
文件中注释 gRPC 方法:
syntax = "proto3";
option csharp_namespace = "GrpcServiceTranscoding";
import "google/api/annotations.proto";
package greet;
// The greeting service definition.
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
get: "/v1/greeter/{name}"
};
}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
SayHello
gRPC 方法现在可以作为 gRPC 和 JSON Web API 调用:
- 请求:
GET /v1/greeter/world
- 响应:
{ "message": "Hello world" }
如果服务器配置为针对每个请求写入日志,则服务器日志显示 gRPC 服务执行 HTTP 调用。 转码将传入的 HTTP 请求映射到 gRPC 消息,然后将响应消息转换为 JSON。
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET https://localhost:5001/v1/greeter/world
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'gRPC - /v1/greeter/{name}'
info: Server.GreeterService[0]
Sending hello to world
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'gRPC - /v1/greeter/{name}'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 1.996ms 200 application/json
批注 gRPC 方法
gRPC 方法必须在支持转码之前使用 HTTP 规则进行注释。 HTTP 规则包括有关如何调用 gRPC 方法的信息,例如 HTTP 方法和路由。
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
get: "/v1/greeter/{name}"
};
}
}
继续浏览示例:
- 使用
SayHello
方法定义Greeter
服务。 该方法具有使用名称google.api.http
指定的 HTTP 规则。 - 可以使用
GET
请求和/v1/greeter/{name}
路由访问该方法。 - 请求消息上的
name
字段绑定到路由参数。
许多选项可用于自定义 gRPC 方法绑定到 RESTful API 的方式。 有关批注 gRPC 方法和自定义 JSON 的详细信息,请参阅为 gRPC JSON 转码配置 HTTP 和 JSON。
流式处理方法
HTTP/2 上的传统 gRPC 支持所有方向的流式处理。 转码仅限于服务器流式处理。 不支持客户端流式处理和双向流式处理方法。
服务器流式处理方法使用以行分隔的 JSON。 使用 WriteAsync
写入的每条消息都会序列化为 JSON,并在后面换一行。
以下服务器流式处理方法写入三条消息:
public override async Task StreamingFromServer(ExampleRequest request,
IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
for (var i = 1; i <= 3; i++)
{
await responseStream.WriteAsync(new ExampleResponse { Text = $"Message {i}" });
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
客户端接收三个以行分隔的 JSON 对象:
{"Text":"Message 1"}
{"Text":"Message 2"}
{"Text":"Message 3"}
请注意,WriteIndented
JSON 设置不适用于服务器流式处理方法。 整齐打印将新行和空格添加到 JSON,无法与以行分隔的 JSON 一起使用。
查看或下载 ASP.NET Core gPRC 转码和流式处理应用示例。
HTTP 协议
.NET SDK 中包含的 ASP.NET Core gRPC 服务模板创建仅针对 HTTP/2 配置的应用。 当应用仅支持传统的 gRPC over HTTP/2 时,HTTP/2 是很好的默认设置。 但是,转码同时适用于 HTTP/1.1 和 HTTP/2。 某些平台(如 UWP 或 Unity)无法使用 HTTP/2。 若要支持所有客户端应用,请将服务器配置为启用 HTTP/1.1 和 HTTP/2。
更新 appsettings.json
中的默认协议:
{
"Kestrel": {
"EndpointDefaults": {
"Protocols": "Http1AndHttp2"
}
}
}
在同一端口上启用 HTTP/1.1 和 HTTP/2 需要 TLS 进行协议协商。 有关在 gRPC 应用中配置 HTTP 协议的详细信息,请参阅 ASP.NET Core gRPC 协议协商。
gRPC JSON 转码与 gRPC-Web
转码和 gRPC-Web 都支持从浏览器调用 gRPC 服务。 但是,它们的操作方式是不同的:
- gRPC-Web 允许浏览器应用通过 gRPC-Web 客户端和 Protobuf 从浏览器调用 gRPC 服务。 gRPC-Web 需要浏览器应用生成 gRPC 客户端,并且具有快速发送小型 Protobuf 消息的优点。
- 转码允许浏览器应用调用 gRPC 服务,就像使用 JSON 调用 RESTful API 一样。 浏览器应用不需要生成 gRPC 客户端或了解 gRPC 的任何信息。
可以使用浏览器 JavaScript API 调用以前的 Greeter
服务:
var name = nameInput.value;
fetch('/v1/greeter/' + name)
.then((response) => response.json())
.then((result) => {
console.log(result.message);
// Hello world
});
grpc-gateway
Grpc-gateway 是从 grpc 服务创建 RESTful JSON API 的另一种技术。 它使用相同的 .proto
注释将 HTTP 概念映射到 gRPC 服务。
grpc-gateway 使用代码生成来创建反向代理服务器。 反向代理将 RESTful 调用转换为 gRPC+Protobuf,然后通过 HTTP/2 将调用发送到 gRPC 服务。 这种方法的优点是 gRPC 服务不知道 RESTful JSON API。 任何 gRPC 服务器都可以使用 grpc-gateway。
同时,gRPC JSON 转码在 ASP.NET Core 应用内运行。 它将 JSON 反序列化为 Protobuf 消息,然后直接调用 gRPC 服务。 ASP.NET Core 中的转码为 .NET 应用开发人员提供了以下优势:
- 复杂性更低:gRPC 服务和映射的 RESTful JSON API 都在同一个 ASP.NET Core 应用外部运行。
- 性能更佳:转码将 JSON 反序列化为 Protobuf 消息,并直接调用 gRPC 服务。 与对不同的服务器进行新的 gRPC 调用相比,执行此进程内操作时性能有显著的优势。
- 成本更低:减少了服务器数量,降低了每月托管费用。
如需了解 grpc-gateway 的安装和使用,请参阅 grpc-gateway 自述文件。