ASP.NET Core gRPC 应用中的 gRPC-Web
注意
此版本不是本文的最新版本。 对于当前版本,请参阅此文的 .NET 8 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 对于当前版本,请参阅此文的 .NET 8 版本。
了解如何配置现有 ASP.NET Core gRPC 服务,以使其可以从使用 gRPC-Web 协议的浏览器应用中进行调用。 gRPC-Web 允许浏览器 JavaScript 和 Blazor 应用调用 gRPC 服务。 无法从基于浏览器的应用中调用 HTTP/2 gRPC 服务。 可将托管于 ASP.NET Core 中的 gRPC 服务配置为随 HTTP/2 gRPC 一起支持 gRPC-Web。
有关将 gRPC 服务添加到现有 ASP.NET Core 应用的说明,请参阅将 gRPC 服务添加到 ASP.NET Core 应用。
有关创建 gRPC 项目的说明,请参阅在 ASP.NET Core 中创建 .Net Core gRPC 客户端和服务器。
ASP.NET Core gRPC-Web 与 Envoy
有两种方式可将 gRPC-Web 添加到 ASP.NET Core 应用中:
- 在 ASP.NET Core 中同时支持 gRPC-Web 和 gRPC HTTP/2。 此选项会使用
Grpc.AspNetCore.Web
包提供的中间件。 - 使用 Envoy 代理的 gRPC-Web 支持将 gRPC-Web 转换为 gRPC HTTP/2。 转换后的调用随后会转发给 ASP.NET Core 应用。
每种方法既有优点,也有缺点。 如果应用的环境已将 Envoy 用作代理,则也可以使用 Envoy 提供 gRPC-Web 支持。 对于仅需要 ASP.NET Core 的 gRPC-Web 的基本解决方案,Grpc.AspNetCore.Web
是一个不错的选择。
配置 ASP.NET Core 中的 gRPC-Web
可将托管于 ASP.NET Core 中的 gRPC 服务配置为随 HTTP/2 gRPC 一起支持 gRPC-Web。 gRPC-Web 不需要对服务进行任何更改。 唯一的修改是在 Program.cs
设置中间件。
若要使用 ASP.NET Core gRPC 服务启用 gRPC-Web:
- 添加对
Grpc.AspNetCore.Web
包的引用。 - 配置应用以使用 gRPC-Web,方法是将
UseGrpcWeb
和EnableGrpcWeb
添加到Program.cs
:
using GrpcGreeter.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();
app.UseGrpcWeb();
app.MapGrpcService<GreeterService>().EnableGrpcWeb();
app.MapGet("/", () => "This gRPC service is gRPC-Web enabled and is callable from browser apps using the gRPC-Web protocol");
app.Run();
前面的代码:
- 在路由之后、终结点之前添加 gRPC-Web 中间件
UseGrpcWeb
。 - 指定
endpoints.MapGrpcService<GreeterService>()
方法支持带有EnableGrpcWeb
的 gRPC-Web。
或者,可以配置 gRPC-Web 中间件,这样所有服务在默认情况下都支持 gRPC-Web,而不需要 EnableGrpcWeb
。 在添加中间件时指定 new GrpcWebOptions { DefaultEnabled = true }
。
using GrpcGreeter.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();
app.UseGrpcWeb(new GrpcWebOptions { DefaultEnabled = true });
app.MapGrpcService<GreeterService>().EnableGrpcWeb();
app.MapGet("/", () => "All gRPC service are supported by default in this example, and are callable from browser apps using the gRPC-Web protocol");
app.Run();
注意
存在一个已知问题,它导致在 .NET Core 3.x 中由 HTTP.sys 托管时 gRPC-Web 会失败。
Grpc-web experimental and UseHttpSys()? (grpc/grpc-dotnet #853) 中提供了用于在 HTTP.sys 上使用 gRPC-Web 的解决方法。
gRPC-Web 和 CORS
浏览器安全性可防止网页向不处理网页的域发送请求。 此限制适用于使用浏览器应用发出 gRPC-Web 调用。 例如,由 https://www.contoso.com
提供服务的浏览器应用对托管于 https://services.contoso.com
上的 gRPC-Web 服务的调用会被阻止。 跨域资源共享 (CORS) 可用于放宽此限制。
若要允许浏览器应用进行跨域 gRPC-Web 调用,请在 ASP.NET Core 中设置 CORS。 使用内置 CORS 支持,并使用 WithExposedHeaders 公开特定于 gRPC 的标头。
using GrpcGreeter.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
builder.Services.AddCors(o => o.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.WithExposedHeaders("Grpc-Status", "Grpc-Message", "Grpc-Encoding", "Grpc-Accept-Encoding");
}));
var app = builder.Build();
app.UseGrpcWeb();
app.UseCors();
app.MapGrpcService<GreeterService>().EnableGrpcWeb()
.RequireCors("AllowAll");
app.MapGet("/", () => "This gRPC service is gRPC-Web enabled, CORS enabled, and is callable from browser apps using the gRPC-Web protocol");
app.Run();
前面的代码:
- 调用
AddCors
以添加 CORS 服务,并配置公开特定于 gRPC 的标头的 CORS 策略。 - 调用
UseCors
以在路由配置之后、终结点配置之前添加 CORS 中间件。 - 指定
endpoints.MapGrpcService<GreeterService>()
方法支持带有RequireCors
的 CORS。
gRPC-Web 和流式处理
传统的 gRPC over HTTP/2 支持客户端、服务器和双向流式处理。 gRPC-Web 对流式处理提供有限的支持:
- gRPC-Web 浏览器客户端不支持调用客户端流式处理和双向流式处理方法。
- gRPC-Web .NET 客户端不支持通过 HTTP/1.1 调用客户端流式处理和双向流式处理方法。
- 托管在 Azure 应用服务和 IIS 上的 ASP.NET Core gRPC 服务不支持双向流式处理。
使用 gRPC 时,仅建议使用一元方法和服务器流式处理方法。
HTTP 协议
.NET SDK 中包含的 ASP.NET Core gRPC 服务模板创建仅针对 HTTP/2 配置的应用。 当应用仅支持传统的 gRPC over HTTP/2 时,这是很好的默认设置。 但是,gRPC-Web 同时适用于 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 进行协议协商。 有关详细信息,请参阅 ASP.NET Core gRPC 协议协商。
从浏览器调用 gRPC-Web
浏览器应用可以使用 gRPC-Web 来调用 gRPC 服务。 使用 gRPC-Web 从浏览器调用 gRPC 服务时,存在一些要求和限制:
- 服务器必须包含支持 gRPC-Web 的配置。
- 不支持客户端流式处理和双向流式处理调用。 支持服务器流式处理。
- 在其他域上调用 gRPC 服务需要在服务器上配置 CORS。
JavaScript gRPC-Web 客户端
存在 JavaScript gRPC-Web 客户端。 有关如何从 JavaScript 使用 gRPC-Web 的说明,请参阅使用 gRPC-Web 编写 JavaScript 客户端代码。
使用 .NET gRPC 客户端配置 gRPC-Web
可以将 .NET gRPC 客户端配置为发出 gRPC-Web 调用。 这对于托管在浏览器中且具有相同 JavaScript 代码 HTTP 限制的 Blazor WebAssembly 应用来说非常有用。 使用 .NET 客户端调用 gRPC-Web 与 HTTP/2 gRPC 相同。 唯一的修改是创建通道的方式。
若要使用 gRPC-Web:
- 添加对
Grpc.Net.Client.Web
包的引用。 - 确保对
Grpc.Net.Client
包的引用为版本 2.29.0 或更高版本。 - 配置通道以使用
GrpcWebHandler
:
var channel = GrpcChannel.ForAddress("https://localhost:53305", new GrpcChannelOptions
{
HttpHandler = new GrpcWebHandler(new HttpClientHandler())
});
var client = new Greeter.GreeterClient(channel);
var response = await client.SayHelloAsync(
new HelloRequest { Name = "GreeterClient" });
前面的代码:
- 配置通道以使用 gRPC-Web。
- 创建一个客户端并使用通道发出调用。
GrpcWebHandler
具有以下配置选项:
InnerHandler
:发出 gRPC HTTP 请求的基础 HttpMessageHandler,例如HttpClientHandler
。GrpcWebMode
:枚举类型,指定 gRPC HTTP 请求Content-Type
是application/grpc-web
还是application/grpc-web-text
。GrpcWebMode.GrpcWeb
配置在不编码的情况下发送内容。 默认值。GrpcWebMode.GrpcWebText
配置 base64 编码的内容。 对于浏览器中的服务器流式处理调用是必需的。
HttpVersion
:HTTP 协议Version
用于在基础 gRPC HTTP 请求上设置 HttpRequestMessage.Version。 gRPC-Web 不需要特定版本,且除非指定,否则不会替代默认版本。
重要
生成的 gRPC 客户端具有用于调用一元方法的同步和异步方法。 例如,SayHello
是同步,SayHelloAsync
是异步。 Blazor WebAssembly 中始终需要异步方法。 在 Blazor WebAssembly 应用中调用同步方法将导致应用无响应。
将 gRPC-Web 与 gRPC 客户端工厂一起使用
使用 gRPC 客户端工厂创建与 gRPC-Web 兼容的 .NET 客户端:
- 将包引用添加到以下包的项目文件中:
- 使用泛型
AddGrpcClient
扩展方法,通过依赖项注入 (DI) 注册 gRPC 客户端。 在 Blazor WebAssembly 应用中,服务在Program.cs
中通过 DI 注册。 - 使用 ConfigurePrimaryHttpMessageHandler 扩展方法配置
GrpcWebHandler
。
builder.Services
.AddGrpcClient<Greet.GreeterClient>(options =>
{
options.Address = new Uri("https://localhost:5001");
})
.ConfigurePrimaryHttpMessageHandler(
() => new GrpcWebHandler(new HttpClientHandler()));
有关详细信息,请参阅 .NET 中的 gRPC 客户端工厂集成。
其他资源
了解如何配置现有 ASP.NET Core gRPC 服务,以使其可以从使用 gRPC-Web 协议的浏览器应用中进行调用。 gRPC-Web 允许浏览器 JavaScript 和 Blazor 应用调用 gRPC 服务。 无法从基于浏览器的应用中调用 HTTP/2 gRPC 服务。 可将托管于 ASP.NET Core 中的 gRPC 服务配置为随 HTTP/2 gRPC 一起支持 gRPC-Web。
有关将 gRPC 服务添加到现有 ASP.NET Core 应用的说明,请参阅将 gRPC 服务添加到 ASP.NET Core 应用。
有关创建 gRPC 项目的说明,请参阅在 ASP.NET Core 中创建 .Net Core gRPC 客户端和服务器。
ASP.NET Core gRPC-Web 与 Envoy
有两种方式可将 gRPC-Web 添加到 ASP.NET Core 应用中:
- 在 ASP.NET Core 中同时支持 gRPC-Web 和 gRPC HTTP/2。 此选项会使用
Grpc.AspNetCore.Web
包提供的中间件。 - 使用 Envoy 代理的 gRPC-Web 支持将 gRPC-Web 转换为 gRPC HTTP/2。 转换后的调用随后会转发给 ASP.NET Core 应用。
每种方法既有优点,也有缺点。 如果应用的环境已将 Envoy 用作代理,则也可以使用 Envoy 提供 gRPC-Web 支持。 对于仅需要 ASP.NET Core 的 gRPC-Web 的基本解决方案,Grpc.AspNetCore.Web
是一个不错的选择。
配置 ASP.NET Core 中的 gRPC-Web
可将托管于 ASP.NET Core 中的 gRPC 服务配置为随 HTTP/2 gRPC 一起支持 gRPC-Web。 gRPC-Web 不需要对服务进行任何更改。 唯一的修改是在 Program.cs
设置中间件。
若要使用 ASP.NET Core gRPC 服务启用 gRPC-Web:
- 添加对
Grpc.AspNetCore.Web
包的引用。 - 配置应用以使用 gRPC-Web,方法是将
UseGrpcWeb
和EnableGrpcWeb
添加到Program.cs
:
using GrpcGreeter.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();
app.UseGrpcWeb();
app.MapGrpcService<GreeterService>().EnableGrpcWeb();
app.MapGet("/", () => "This gRPC service is gRPC-Web enabled and is callable from browser apps using the gRPC-Web protocol");
app.Run();
前面的代码:
- 在路由之后、终结点之前添加 gRPC-Web 中间件
UseGrpcWeb
。 - 指定
endpoints.MapGrpcService<GreeterService>()
方法支持带有EnableGrpcWeb
的 gRPC-Web。
或者,可以配置 gRPC-Web 中间件,这样所有服务在默认情况下都支持 gRPC-Web,而不需要 EnableGrpcWeb
。 在添加中间件时指定 new GrpcWebOptions { DefaultEnabled = true }
。
using GrpcGreeter.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();
app.UseGrpcWeb(new GrpcWebOptions { DefaultEnabled = true });
app.MapGrpcService<GreeterService>().EnableGrpcWeb();
app.MapGet("/", () => "All gRPC service are supported by default in this example, and are callable from browser apps using the gRPC-Web protocol");
app.Run();
注意
存在一个已知问题,它导致在 .NET Core 3.x 中由 HTTP.sys 托管时 gRPC-Web 会失败。
Grpc-web experimental and UseHttpSys()? (grpc/grpc-dotnet #853) 中提供了用于在 HTTP.sys 上使用 gRPC-Web 的解决方法。
gRPC-Web 和 CORS
浏览器安全性可防止网页向不处理网页的域发送请求。 此限制适用于使用浏览器应用发出 gRPC-Web 调用。 例如,由 https://www.contoso.com
提供服务的浏览器应用对托管于 https://services.contoso.com
上的 gRPC-Web 服务的调用会被阻止。 跨域资源共享 (CORS) 可用于放宽此限制。
若要允许浏览器应用进行跨域 gRPC-Web 调用,请在 ASP.NET Core 中设置 CORS。 使用内置 CORS 支持,并使用 WithExposedHeaders 公开特定于 gRPC 的标头。
using GrpcGreeter.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
builder.Services.AddCors(o => o.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.WithExposedHeaders("Grpc-Status", "Grpc-Message", "Grpc-Encoding", "Grpc-Accept-Encoding");
}));
var app = builder.Build();
app.UseGrpcWeb();
app.UseCors();
app.MapGrpcService<GreeterService>().EnableGrpcWeb()
.RequireCors("AllowAll");
app.MapGet("/", () => "This gRPC service is gRPC-Web enabled, CORS enabled, and is callable from browser apps using the gRPC-Web protocol");
app.Run();
前面的代码:
- 调用
AddCors
以添加 CORS 服务,并配置公开特定于 gRPC 的标头的 CORS 策略。 - 调用
UseCors
以在路由配置之后、终结点配置之前添加 CORS 中间件。 - 指定
endpoints.MapGrpcService<GreeterService>()
方法支持带有RequireCors
的 CORS。
gRPC-Web 和流式处理
传统的 gRPC over HTTP/2 支持客户端、服务器和双向流式处理。 gRPC-Web 对流式处理提供有限的支持:
- gRPC-Web 浏览器客户端不支持调用客户端流式处理和双向流式处理方法。
- gRPC-Web .NET 客户端不支持通过 HTTP/1.1 调用客户端流式处理和双向流式处理方法。
- 托管在 Azure 应用服务和 IIS 上的 ASP.NET Core gRPC 服务不支持双向流式处理。
使用 gRPC 时,仅建议使用一元方法和服务器流式处理方法。
HTTP 协议
.NET SDK 中包含的 ASP.NET Core gRPC 服务模板创建仅针对 HTTP/2 配置的应用。 当应用仅支持传统的 gRPC over HTTP/2 时,这是很好的默认设置。 但是,gRPC-Web 同时适用于 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 进行协议协商。 有关详细信息,请参阅 ASP.NET Core gRPC 协议协商。
从浏览器调用 gRPC-Web
浏览器应用可以使用 gRPC-Web 来调用 gRPC 服务。 使用 gRPC-Web 从浏览器调用 gRPC 服务时,存在一些要求和限制:
- 服务器必须包含支持 gRPC-Web 的配置。
- 不支持客户端流式处理和双向流式处理调用。 支持服务器流式处理。
- 在其他域上调用 gRPC 服务需要在服务器上配置 CORS。
JavaScript gRPC-Web 客户端
存在 JavaScript gRPC-Web 客户端。 有关如何从 JavaScript 使用 gRPC-Web 的说明,请参阅使用 gRPC-Web 编写 JavaScript 客户端代码。
使用 .NET gRPC 客户端配置 gRPC-Web
可以将 .NET gRPC 客户端配置为发出 gRPC-Web 调用。 这对于托管在浏览器中且具有相同 JavaScript 代码 HTTP 限制的 Blazor WebAssembly 应用来说非常有用。 使用 .NET 客户端调用 gRPC-Web 与 HTTP/2 gRPC 相同。 唯一的修改是创建通道的方式。
若要使用 gRPC-Web:
- 添加对
Grpc.Net.Client.Web
包的引用。 - 确保对
Grpc.Net.Client
包的引用为版本 2.29.0 或更高版本。 - 配置通道以使用
GrpcWebHandler
:
var channel = GrpcChannel.ForAddress("https://localhost:53305", new GrpcChannelOptions
{
HttpHandler = new GrpcWebHandler(new HttpClientHandler())
});
var client = new Greeter.GreeterClient(channel);
var response = await client.SayHelloAsync(
new HelloRequest { Name = "GreeterClient" });
前面的代码:
- 配置通道以使用 gRPC-Web。
- 创建一个客户端并使用通道发出调用。
GrpcWebHandler
具有以下配置选项:
InnerHandler
:发出 gRPC HTTP 请求的基础 HttpMessageHandler,例如HttpClientHandler
。GrpcWebMode
:枚举类型,指定 gRPC HTTP 请求Content-Type
是application/grpc-web
还是application/grpc-web-text
。GrpcWebMode.GrpcWeb
配置在不编码的情况下发送内容。 默认值。GrpcWebMode.GrpcWebText
配置 base64 编码的内容。 对于浏览器中的服务器流式处理调用是必需的。
HttpVersion
:HTTP 协议Version
用于在基础 gRPC HTTP 请求上设置 HttpRequestMessage.Version。 gRPC-Web 不需要特定版本,且除非指定,否则不会替代默认版本。
重要
生成的 gRPC 客户端具有用于调用一元方法的同步和异步方法。 例如,SayHello
是同步,SayHelloAsync
是异步。 Blazor WebAssembly 中始终需要异步方法。 在 Blazor WebAssembly 应用中调用同步方法将导致应用无响应。
将 gRPC-Web 与 gRPC 客户端工厂一起使用
使用 gRPC 客户端工厂创建与 gRPC-Web 兼容的 .NET 客户端:
- 将包引用添加到以下包的项目文件中:
- 使用泛型
AddGrpcClient
扩展方法,通过依赖项注入 (DI) 注册 gRPC 客户端。 在 Blazor WebAssembly 应用中,服务在Program.cs
中通过 DI 注册。 - 使用 ConfigurePrimaryHttpMessageHandler 扩展方法配置
GrpcWebHandler
。
builder.Services
.AddGrpcClient<Greet.GreeterClient>(options =>
{
options.Address = new Uri("https://localhost:5001");
})
.ConfigurePrimaryHttpMessageHandler(
() => new GrpcWebHandler(new HttpClientHandler()));
有关详细信息,请参阅 .NET 中的 gRPC 客户端工厂集成。
其他资源
了解如何配置现有 ASP.NET Core gRPC 服务,以使其可以从使用 gRPC-Web 协议的浏览器应用中进行调用。 gRPC-Web 允许浏览器 JavaScript 和 Blazor 应用调用 gRPC 服务。 无法从基于浏览器的应用中调用 HTTP/2 gRPC 服务。 可将托管于 ASP.NET Core 中的 gRPC 服务配置为随 HTTP/2 gRPC 一起支持 gRPC-Web。
有关将 gRPC 服务添加到现有 ASP.NET Core 应用的说明,请参阅将 gRPC 服务添加到 ASP.NET Core 应用。
有关创建 gRPC 项目的说明,请参阅在 ASP.NET Core 中创建 .Net Core gRPC 客户端和服务器。
ASP.NET Core gRPC-Web 与 Envoy
有两种方式可将 gRPC-Web 添加到 ASP.NET Core 应用中:
- 在 ASP.NET Core 中同时支持 gRPC-Web 和 gRPC HTTP/2。 此选项会使用
Grpc.AspNetCore.Web
包提供的中间件。 - 使用 Envoy 代理的 gRPC-Web 支持将 gRPC-Web 转换为 gRPC HTTP/2。 转换后的调用随后会转发给 ASP.NET Core 应用。
每种方法既有优点,也有缺点。 如果应用的环境已将 Envoy 用作代理,则也可以使用 Envoy 提供 gRPC-Web 支持。 对于仅需要 ASP.NET Core 的 gRPC-Web 的基本解决方案,Grpc.AspNetCore.Web
是一个不错的选择。
配置 ASP.NET Core 中的 gRPC-Web
可将托管于 ASP.NET Core 中的 gRPC 服务配置为随 HTTP/2 gRPC 一起支持 gRPC-Web。 gRPC-Web 不需要对服务进行任何更改。 唯一的修改是启动配置。
若要使用 ASP.NET Core gRPC 服务启用 gRPC-Web:
- 添加对
Grpc.AspNetCore.Web
包的引用。 - 配置应用以使用 gRPC-Web,方法是将
UseGrpcWeb
和EnableGrpcWeb
添加到Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseGrpcWeb(); // Must be added between UseRouting and UseEndpoints
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<GreeterService>().EnableGrpcWeb();
});
}
前面的代码:
- 在路由之后、终结点之前添加 gRPC-Web 中间件
UseGrpcWeb
。 - 指定
endpoints.MapGrpcService<GreeterService>()
方法支持带有EnableGrpcWeb
的 gRPC-Web。
或者,可以配置 gRPC-Web 中间件,这样所有服务在默认情况下都支持 gRPC-Web,而不需要 EnableGrpcWeb
。 在添加中间件时指定 new GrpcWebOptions { DefaultEnabled = true }
。
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseGrpcWeb(new GrpcWebOptions { DefaultEnabled = true });
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<GreeterService>();
});
}
}
注意
存在一个已知问题,它导致在 .NET Core 3.x 中由 HTTP.sys 托管时 gRPC-Web 会失败。
Grpc-web experimental and UseHttpSys()? (grpc/grpc-dotnet #853) 中提供了用于在 HTTP.sys 上使用 gRPC-Web 的解决方法。
gRPC-Web 和 CORS
浏览器安全性可防止网页向不处理网页的域发送请求。 此限制适用于使用浏览器应用发出 gRPC-Web 调用。 例如,由 https://www.contoso.com
提供服务的浏览器应用对托管于 https://services.contoso.com
上的 gRPC-Web 服务的调用会被阻止。 跨域资源共享 (CORS) 可用于放宽此限制。
若要允许浏览器应用进行跨域 gRPC-Web 调用,请在 ASP.NET Core 中设置 CORS。 使用内置 CORS 支持,并使用 WithExposedHeaders 公开特定于 gRPC 的标头。
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
services.AddCors(o => o.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.WithExposedHeaders("Grpc-Status", "Grpc-Message", "Grpc-Encoding", "Grpc-Accept-Encoding");
}));
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseGrpcWeb();
app.UseCors();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<GreeterService>().EnableGrpcWeb()
.RequireCors("AllowAll");
});
}
前面的代码:
- 调用
AddCors
以添加 CORS 服务,并配置公开特定于 gRPC 的标头的 CORS 策略。 - 调用
UseCors
以在路由配置之后、终结点配置之前添加 CORS 中间件。 - 指定
endpoints.MapGrpcService<GreeterService>()
方法支持带有RequireCors
的 CORS。
gRPC-Web 和流式处理
传统的 gRPC over HTTP/2 支持客户端、服务器和双向流式处理。 gRPC-Web 对流式处理提供有限的支持:
- gRPC-Web 浏览器客户端不支持调用客户端流式处理和双向流式处理方法。
- gRPC-Web .NET 客户端不支持通过 HTTP/1.1 调用客户端流式处理和双向流式处理方法。
- 托管在 Azure 应用服务和 IIS 上的 ASP.NET Core gRPC 服务不支持双向流式处理。
使用 gRPC 时,仅建议使用一元方法和服务器流式处理方法。
HTTP 协议
.NET SDK 中包含的 ASP.NET Core gRPC 服务模板创建仅针对 HTTP/2 配置的应用。 当应用仅支持传统的 gRPC over HTTP/2 时,这是很好的默认设置。 但是,gRPC-Web 同时适用于 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 进行协议协商。 有关详细信息,请参阅 ASP.NET Core gRPC 协议协商。
从浏览器调用 gRPC-Web
浏览器应用可以使用 gRPC-Web 来调用 gRPC 服务。 使用 gRPC-Web 从浏览器调用 gRPC 服务时,存在一些要求和限制:
- 服务器必须包含支持 gRPC-Web 的配置。
- 不支持客户端流式处理和双向流式处理调用。 支持服务器流式处理。
- 在其他域上调用 gRPC 服务需要在服务器上配置 CORS。
JavaScript gRPC-Web 客户端
存在 JavaScript gRPC-Web 客户端。 有关如何从 JavaScript 使用 gRPC-Web 的说明,请参阅使用 gRPC-Web 编写 JavaScript 客户端代码。
使用 .NET gRPC 客户端配置 gRPC-Web
可以将 .NET gRPC 客户端配置为发出 gRPC-Web 调用。 这对于托管在浏览器中且具有相同 JavaScript 代码 HTTP 限制的 Blazor WebAssembly 应用来说非常有用。 使用 .NET 客户端调用 gRPC-Web 与 HTTP/2 gRPC 相同。 唯一的修改是创建通道的方式。
若要使用 gRPC-Web:
- 添加对
Grpc.Net.Client.Web
包的引用。 - 确保对
Grpc.Net.Client
包的引用为版本 2.29.0 或更高版本。 - 配置通道以使用
GrpcWebHandler
:
var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
HttpHandler = new GrpcWebHandler(new HttpClientHandler())
});
var client = new Greeter.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });
前面的代码:
- 配置通道以使用 gRPC-Web。
- 创建一个客户端并使用通道发出调用。
GrpcWebHandler
具有以下配置选项:
InnerHandler
:发出 gRPC HTTP 请求的基础 HttpMessageHandler,例如HttpClientHandler
。GrpcWebMode
:枚举类型,指定 gRPC HTTP 请求Content-Type
是application/grpc-web
还是application/grpc-web-text
。GrpcWebMode.GrpcWeb
配置在不编码的情况下发送内容。 默认值。GrpcWebMode.GrpcWebText
配置 base64 编码的内容。 对于浏览器中的服务器流式处理调用是必需的。
HttpVersion
:HTTP 协议Version
用于在基础 gRPC HTTP 请求上设置 HttpRequestMessage.Version。 gRPC-Web 不需要特定版本,且除非指定,否则不会替代默认版本。
重要
生成的 gRPC 客户端具有用于调用一元方法的同步和异步方法。 例如,SayHello
是同步,SayHelloAsync
是异步。 Blazor WebAssembly 中始终需要异步方法。 在 Blazor WebAssembly 应用中调用同步方法将导致应用无响应。
将 gRPC-Web 与 gRPC 客户端工厂一起使用
使用 gRPC 客户端工厂创建与 gRPC-Web 兼容的 .NET 客户端:
- 将包引用添加到以下包的项目文件中:
- 使用泛型
AddGrpcClient
扩展方法,通过依赖项注入 (DI) 注册 gRPC 客户端。 在 Blazor WebAssembly 应用中,服务在Program.cs
中通过 DI 注册。 - 使用 ConfigurePrimaryHttpMessageHandler 扩展方法配置
GrpcWebHandler
。
builder.Services
.AddGrpcClient<Greet.GreeterClient>(options =>
{
options.Address = new Uri("https://localhost:5001");
})
.ConfigurePrimaryHttpMessageHandler(
() => new GrpcWebHandler(new HttpClientHandler()));
有关详细信息,请参阅 .NET 中的 gRPC 客户端工厂集成。