ASP.NET Core gRPC 应用中的 gRPC-Web

作者:James Newton-King

了解如何配置现有 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,方法是将 UseGrpcWebEnableGrpcWeb 添加到 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 uisng the gRPC-Web protocal");

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 uisng the gRPC-Web protocal");

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 uisng the gRPC-Web protocal");

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"
    }
  }
}

或者,在启动代码中配置 Kestrel 终结点

在同一端口上启用 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:

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-Typeapplication/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 客户端:

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,方法是将 UseGrpcWebEnableGrpcWeb 添加到 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 uisng the gRPC-Web protocal");

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 uisng the gRPC-Web protocal");

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 uisng the gRPC-Web protocal");

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"
    }
  }
}

或者,在启动代码中配置 Kestrel 终结点

在同一端口上启用 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:

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-Typeapplication/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 客户端:

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,方法是将 UseGrpcWebEnableGrpcWeb 添加到 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"
    }
  }
}

或者,在启动代码中配置 Kestrel 终结点

在同一端口上启用 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:

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-Typeapplication/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 客户端:

builder.Services
    .AddGrpcClient<Greet.GreeterClient>(options =>
    {
        options.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(
        () => new GrpcWebHandler(new HttpClientHandler()));

有关详细信息,请参阅 .NET 中的 gRPC 客户端工厂集成

其他资源