添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
打酱油的松球  ·  windows 下载 grpc - 简书·  1 年前    · 
天涯  ·  Kong(API网关) - 知乎·  2 年前    · 
9976254  ·  grpc 报错解决 code = ...·  3 年前    · 
寂寞的茄子  ·  @ExceptionHandler ...·  3 周前    · 
瘦瘦的烤红薯  ·  【JSP EL】<c:if> ...·  5 月前    · 
紧张的烤面包  ·  Open ...·  1 年前    · 

本文档讨论了在 .NET 上开发 gRPC 应用时经常遇到的问题。

客户端和服务 SSL/TLS 配置不匹配

默认情况下,gRPC 模板和示例使用 传输层安全性 (TLS) 来保护 gRPC 服务。 gRPC 客户端需要使用安全连接才能成功调用受保护的 gRPC 服务。

可在应用启动时验证 ASP.NET Core gRPC 服务是否正在使用 TLS。 该服务将在 HTTPS 终结点上侦听:

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

.NET Core 客户端必须在服务器地址中使用 https 才能使用安全连接进行调用:

var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);

所有 gRPC 客户端实现都支持 TLS。 其他语言的 gRPC 客户端通常需要配置有 SslCredentials 的通道。 SslCredentials 指定客户端将使用的证书,必须使用该证书,而不是使用不安全凭据。 有关配置不同 gRPC 客户端实现以使用 TLS 的示例,请参阅 gRPC 身份验证

使用不受信任/无效证书调用 gRPC 服务

.NET gRPC 客户端要求服务具有受信任的证书。 在没有受信任证书的情况下调用 gRPC 服务时,将返回以下错误消息:

未经处理的异常。 System.Net.Http.HttpRequestException:无法建立 SSL 连接,请查看内部异常。 ---> System.Security.Authentication.AuthenticationException:根据验证过程,远程证书无效。

如果在本地测试应用且 ASP.NET Core HTTPS 开发证书不受信任,则可能会显示此错误。 有关解决此问题的说明,请参阅在 Windows 和 macOS 上信任 ASP.NET Core HTTPS 开发证书

如果要在另一台计算机上调用 gRPC 服务且无法信任该证书,则可以将 gRPC 客户端配置为忽略无效的证书。 下面的代码使用 HttpClientHandler.ServerCertificateCustomValidationCallback 来允许在没有受信任证书的情况下进行调用:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greeter.GreeterClient(channel);

gRPC 客户端工厂允许在没有受信任证书的情况下调用。 使用 ConfigurePrimaryHttpMessageHandler 扩展方法在客户端上配置处理程序:

var services = new ServiceCollection(); services .AddGrpcClient<Greeter.GreeterClient>(o => o.Address = new Uri("https://localhost:5001"); .ConfigurePrimaryHttpMessageHandler(() => var handler = new HttpClientHandler(); handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; return handler;

不受信任的证书只应在应用开发过程中使用。 生产应用应始终使用有效的证书。

使用 .NET Core 客户端调用不安全的 gRPC 服务

.NET gRPC 可以通过在服务器地址中指定 http 来调用不安全的 gRPC 服务。 例如,GrpcChannel.ForAddress("http://localhost:5000")

根据应用使用的 .NET 版本,调用不安全的 gRPC 服务还需要满足一些额外要求:

  • .NET 5 或更高版本要求使用 Grpc.Net.Client 版本 2.32.0 或更高版本。
  • .NET Core 3.x 要求进行额外配置。 该应用必须将 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 开关设置为 true。 有关详细信息,请参阅 Asp.Net Core 3.x:使用 .NET 客户端调用不安全的 gRPC 服务
  • 必须在仅 HTTP/2 端口上托管不安全的 gRPC 服务。 有关详细信息,请参阅 ASP.NET Core 协议协商

    无法在 macOS 上启动 ASP.NET Core gRPC 应用

    Kestrel 不支持在使用 .NET 8 之前版本的 macOS 上使用带 TLS 的 HTTP/2。 默认情况下,ASP.NET Core gRPC 模板和示例使用 TLS。 尝试启动 gRPC 服务器时,你将看到以下错误消息:

    无法绑定到 IPv4 环回接口上的 https://localhost:5001 :“由于缺少 ALPN 支持,macOS 不支持使用 TLS 的 HTTP/2。”。

    若要解决 .NET 7 及更早版本中的此问题,请将 Kestrel 和 gRPC 客户端配置为使用不带有 TLS 的 HTTP/2。 应仅在开发过程中执行此操作。 如果不使用 TLS,将会在不加密的情况下发送 gRPC 消息。 有关详细信息,请参阅 Asp.Net Core 7.0:无法在 macOS 上启用 ASP.NET Core gRPC 应用

    gRPC C# 资产不是从 .proto 文件生成的代码

    具体客户端和服务基类的 gRPC 代码生成需要从项目引用 protobuf 文件和工具。 必须包括:

  • 要在 <Protobuf> 项目组中使用的 .proto 文件。 导入的 .proto 文件必须由项目引用。
  • 对 gRPC 工具包 Grpc.Tools 的包引用。
  • 有关生成 gRPC C# 资产的详细信息,请参阅使用 C# 的 gRPC 服务

    托管 gRPC 服务的 ASP.NET Core web 应用仅需要已生成的服务基类:

    <ItemGroup>
      <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
    </ItemGroup>
    

    发出 gRPC 调用的 gRPC 客户端应用仅需要已生成的具体客户端:

    <ItemGroup>
      <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
    </ItemGroup>
    

    WPF 项目无法从 .proto 文件生成 gRPC C# 资产

    WPF 项目存在一个已知问题,其阻止 gRPC 代码生成正常运行。 在 WPF 项目中通过引用 Grpc.Tools.proto 文件生成的任何 gRPC 类型在使用时都将创建编译错误:

    错误 CS0246:找不到类型名称或命名空间名称 ’MyGrpcServices’ (是否缺少 using 指令或程序集引用?)

    可以通过以下方式解决此问题:

  • 创建新的 .NET Core 类库项目。
  • 在新项目中,添加引用以.proto 文件启用 C# 代码生成
    • 添加以下包引用:
      • Grpc.Tools
      • Grpc.Net.Client
      • Google.Protobuf
      • .proto 文件添加到 <Protobuf> 项目组。
      • 在 WPF 应用程序中,添加对新项目的引用。
      • WPF 应用程序可以使用来自新类库项目的 gRPC 生成的类型。

        调用子目录中托管的 gRPC 服务

        许多第三方 gRPC 工具不支持子目录中托管的服务。 请考虑找到将 gRPC 托管为根目录的方法。

        发出 gRPC 调用时,将忽略 gRPC 通道地址的路径部分。 例如,当路由对服务的 gRPC 调用时,GrpcChannel.ForAddress("https://localhost:5001/ignored_path") 不使用 ignored_path

        由于 gRPC 具有标准化的规范地址结构,因此会忽略地址路径。 gRPC 地址组合了包、服务和方法的名称:https://localhost:5001/PackageName.ServiceName/MethodName

        在某些情况下,应用需要包含具有 gRPC 调用的路径。 例如,当 ASP.NET Core gRPC 应用托管在 IIS 目录中时,需要在请求中包含该目录。 如果路径是必需的,则可以使用下面指定的自定义 SubdirectoryHandler 方法将其添加到 gRPC 调用:

        public class SubdirectoryHandler : DelegatingHandler
            private readonly string _subdirectory;
            public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
                : base(innerHandler)
                _subdirectory = subdirectory;
            protected override Task<HttpResponseMessage> SendAsync(
                HttpRequestMessage request, CancellationToken cancellationToken)
                var old = request.RequestUri;
                var url = $"{old.Scheme}://{old.Host}:{old.Port}";
                url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
                request.RequestUri = new Uri(url, UriKind.Absolute);
                return base.SendAsync(request, cancellationToken);
        

        SubdirectoryHandler 在创建 gRPC 通道时使用。

        var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");
        var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
        var client = new Greeter.GreeterClient(channel);
        var reply = await client.SayHelloAsync(
                          new HelloRequest { Name = "GreeterClient" });
        

        前面的代码:

      • 创建具有 /MyApp 路径的 SubdirectoryHandler
      • 配置通道以使用 SubdirectoryHandler
      • 使用 SayHelloAsync 调用 gRPC 服务。 gRPC 调用将发送到 https://localhost:5001/MyApp/greet.Greeter/SayHello
      • 或者,可以使用 AddHttpMessageHandler 为客户端工厂配置 SubdirectoryHandler

        将 gRPC 客户端配置为使用 HTTP/3

        .NET gRPC 客户端支持使用 .NET 6 或更高版本的 HTTP/3。 如果服务器向客户端发送表明服务器支持 HTTP/3 的 alt-svc 响应标头,则客户端将自动将其连接升级到 HTTP/3。 有关详细信息,请参阅对 ASP.NET Core Kestrel Web 服务器使用 HTTP/3

        DelegatingHandler 可用于强制 gRPC 客户端使用 HTTP/3。 强制使用 HTTP/3 可以避免升级请求的开销。 使用类似于下面的代码强制使用 HTTP/3:

        public class Http3Handler : DelegatingHandler
            public Http3Handler() { }
            public Http3Handler(HttpMessageHandler innerHandler) : base(innerHandler) { }
            protected override Task<HttpResponseMessage> SendAsync(
                HttpRequestMessage request, CancellationToken cancellationToken)
                request.Version = HttpVersion.Version30;
                request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;
                return base.SendAsync(request, cancellationToken);
        

        Http3Handler 在创建 gRPC 通道时使用。 以下代码创建了一个配置为使用 Http3Handler 的通道。

        var handler = new Http3Handler(new HttpClientHandler());
        var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
        var client = new Greeter.GreeterClient(channel);
        var reply = await client.SayHelloAsync(
                          new HelloRequest { Name = "GreeterClient" });
        

        或者,可以使用 AddHttpMessageHandler 为客户端工厂配置 Http3Handler

        在 Alpine Linux 上构建 gRPC

        Grpc.Tools 包使用称为 protoc 的捆绑的本机二进制文件.proto 文件生成 .NET 类型。 在 Grpc.Tools 中的本机二进制文件(如 Alpine Linux)不支持的平台上构建 gRPC 应用还需执行其他步骤。

        提前生成代码

        一种解决方案可提前生成代码。

      • .proto 文件和 Grpc.Tools 包引用移动到新项目。
      • 将项目作为 NuGet 包发布,然后将其上传到 NuGet 源。
      • 更新应用以引用 NuGet 包。
      • 通过上述步骤,应用不再需要 Grpc.Tools 来构建,因为代码可提前生成。

        自定义 Grpc.Tools 本机二进制文件

        Grpc.Tools 支持使用自定义本机二进制文件。 此功能允许 gRPC 工具在其捆绑的本机二进制文件不支持的环境中运行。

        生成或获取 protocgrpc_csharp_plugin 本机二进制文件,并配置 Grpc.Tools 以使用它们。 通过设置以下环境变量来配置本机二进制文件:

      • PROTOBUF_PROTOC - 协议缓冲区编译器的完整路径
      • GRPC_PROTOC_PLUGIN - grpc_csharp_plugin 的完整路径
      • 对于 Alpine Linux,https://pkgs.alpinelinux.org/ 上有社区提供的用于协议缓冲区编译器和 gRPC 插件的包。

        # Build or install the binaries for your architecture.
        # e.g. for Alpine Linux the grpc-plugins package can be used
        #  See https://pkgs.alpinelinux.org/package/edge/community/x86_64/grpc-plugins
        apk add grpc-plugins  # Alpine Linux specific package installer
        # Set environment variables for the built/installed protoc
        # and grpc_csharp_plugin binaries
        export PROTOBUF_PROTOC=/usr/bin/protoc
        export GRPC_PROTOC_PLUGIN=/usr/bin/grpc_csharp_plugin
        # When dotnet build runs, the Grpc.Tools NuGet package
        # uses the binaries pointed to by the environment variables.
        dotnet build
        

        有关将 Grpc.Tools 与不受支持的体系结构配合使用的详细信息,请参阅 gRPC 构建集成文档

        本文档讨论了在 .NET 上开发 gRPC 应用时经常遇到的问题。

        客户端和服务 SSL/TLS 配置不匹配

        默认情况下,gRPC 模板和示例使用传输层安全性 (TLS) 来保护 gRPC 服务。 gRPC 客户端需要使用安全连接才能成功调用受保护的 gRPC 服务。

        可在应用启动时验证 ASP.NET Core gRPC 服务是否正在使用 TLS。 该服务将在 HTTPS 终结点上侦听:

        info: Microsoft.Hosting.Lifetime[0]
              Now listening on: https://localhost:5001
        info: Microsoft.Hosting.Lifetime[0]
              Application started. Press Ctrl+C to shut down.
        info: Microsoft.Hosting.Lifetime[0]
              Hosting environment: Development
        

        .NET Core 客户端必须在服务器地址中使用 https 才能使用安全连接进行调用:

        static async Task Main(string[] args)
            // The port number(5001) must match the port of the gRPC server.
            var channel = GrpcChannel.ForAddress("https://localhost:5001");
            var client = new Greet.GreeterClient(channel);
        

        所有 gRPC 客户端实现都支持 TLS。 其他语言的 gRPC 客户端通常需要配置有 SslCredentials 的通道。 SslCredentials 指定客户端将使用的证书,必须使用该证书,而不是使用不安全凭据。 有关配置不同 gRPC 客户端实现以使用 TLS 的示例,请参阅 gRPC 身份验证

        使用不受信任/无效证书调用 gRPC 服务

        .NET gRPC 客户端要求服务具有受信任的证书。 在没有受信任证书的情况下调用 gRPC 服务时,将返回以下错误消息:

        未经处理的异常。 System.Net.Http.HttpRequestException:无法建立 SSL 连接,请查看内部异常。 ---> System.Security.Authentication.AuthenticationException:根据验证过程,远程证书无效。

        如果在本地测试应用且 ASP.NET Core HTTPS 开发证书不受信任,则可能会显示此错误。 有关解决此问题的说明,请参阅在 Windows 和 macOS 上信任 ASP.NET Core HTTPS 开发证书

        如果要在另一台计算机上调用 gRPC 服务且无法信任该证书,则可以将 gRPC 客户端配置为忽略无效的证书。 下面的代码使用 HttpClientHandler.ServerCertificateCustomValidationCallback 来允许在没有受信任证书的情况下进行调用:

        var handler = new HttpClientHandler();
        handler.ServerCertificateCustomValidationCallback = 
            HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
        var channel = GrpcChannel.ForAddress("https://localhost:5001",
            new GrpcChannelOptions { HttpHandler = handler });
        var client = new Greet.GreeterClient(channel);
        

        gRPC 客户端工厂允许在没有受信任证书的情况下调用。 使用 ConfigurePrimaryHttpMessageHandler 扩展方法在客户端上配置处理程序:

        builder.Services
            .AddGrpcClient<Greeter.GreeterClient>(o =>
                o.Address = new Uri("https://localhost:5001");
            .ConfigurePrimaryHttpMessageHandler(() =>
                var handler = new HttpClientHandler();
                handler.ServerCertificateCustomValidationCallback = 
                    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
                return handler;
        

        不受信任的证书只应在应用开发过程中使用。 生产应用应始终使用有效的证书。

        使用 .NET Core 客户端调用不安全的 gRPC 服务

        .NET gRPC 可以通过在服务器地址中指定 http 来调用不安全的 gRPC 服务。 例如,GrpcChannel.ForAddress("http://localhost:5000")

        根据应用使用的 .NET 版本,调用不安全的 gRPC 服务还需要满足一些额外要求:

      • .NET 5 或更高版本要求使用 Grpc.Net.Client 版本 2.32.0 或更高版本。

      • .NET Core 3.x 要求进行额外配置。 应用必须将 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 开关设置为 true

        // This switch must be set before creating the GrpcChannel/HttpClient.
        AppContext.SetSwitch(
            "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
        // The port number(5000) must match the port of the gRPC server.
        var channel = GrpcChannel.ForAddress("http://localhost:5000");
        var client = new Greet.GreeterClient(channel);
        

        只有 .NET Core 3.x 需要 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 开关。 .NET 5 中不需要任何额外配置,也没有这项要求。

        必须在仅 HTTP/2 端口上托管不安全的 gRPC 服务。 有关详细信息,请参阅 ASP.NET Core 协议协商

        无法在 macOS 上启动 ASP.NET Core gRPC 应用

        Kestrel 不支持在使用 .NET 8 之前版本的 macOS 上使用带 TLS 的 HTTP/2。 默认情况下,ASP.NET Core gRPC 模板和示例使用 TLS。 尝试启动 gRPC 服务器时,你将看到以下错误消息:

        无法绑定到 IPv4 环回接口上的 https://localhost:5001 :“由于缺少 ALPN 支持,macOS 不支持使用 TLS 的 HTTP/2。”。

        若要解决 .NET 7 及更早版本中的此问题,请将 Kestrel 和 gRPC 客户端配置为使用不带有 TLS 的 HTTP/2。 应仅在开发过程中执行此操作。 如果不使用 TLS,将会在不加密的情况下发送 gRPC 消息。

        Kestrel 必须在 Program.cs 中配置一个不带 TLS 的 HTTP/2 终结点:

        var builder = WebApplication.CreateBuilder(args);
        builder.WebHost.ConfigureKestrel(options =>
            // Setup a HTTP/2 endpoint without TLS.
            options.ListenLocalhost(<5287>, o => o.Protocols =
                HttpProtocols.Http2);
        
      • 在前面的代码中,将 localhost 端口号 5287 替换为在 gRPC 服务项目的 Properties/launchSettings.json 中指定的 HTTP(而不是 HTTPS)端口号。
      • 如果在未带有 TLS 的情况下配置了 HTTP/2 终结点,则终结点的 ListenOptions.Protocols 必须设置为 HttpProtocols.Http2。 无法使用 HttpProtocols.Http1AndHttp2,因为需要使用 TLS 来协商 HTTP/2。 如果未带有 TLS,则与终结点的所有连接均默认为 HTTP/1.1,且 gRPC 调用会失败。

        还必须将 gRPC 客户端配置为不使用 TLS。 有关详细信息,请参阅使用 .NET Core 客户端调用不安全的 gRPC 服务

        应仅在应用开发过程中使用不带有 TLS 的 HTTP/2。 生产应用应始终使用传输安全性。 有关详细信息,请参阅适用于 ASP.NET Core 的 gRPC 的安全注意事项

        gRPC C# 资产不是从 .proto 文件生成的代码

        具体客户端和服务基类的 gRPC 代码生成需要从项目引用 protobuf 文件和工具。 必须包括:

      • 要在 <Protobuf> 项目组中使用的 .proto 文件。 导入的 .proto 文件必须由项目引用。
      • 对 gRPC 工具包 Grpc.Tools 的包引用。
      • 有关生成 gRPC C# 资产的详细信息,请参阅使用 C# 的 gRPC 服务

        托管 gRPC 服务的 ASP.NET Core web 应用仅需要已生成的服务基类:

        <ItemGroup>
          <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
        </ItemGroup>
        

        发出 gRPC 调用的 gRPC 客户端应用仅需要已生成的具体客户端:

        <ItemGroup>
          <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
        </ItemGroup>
        

        WPF 项目无法从 .proto 文件生成 gRPC C# 资产

        WPF 项目存在一个已知问题,其阻止 gRPC 代码生成正常运行。 在 WPF 项目中通过引用 Grpc.Tools.proto 文件生成的任何 gRPC 类型在使用时都将创建编译错误:

        错误 CS0246:找不到类型名称或命名空间名称 ’MyGrpcServices’ (是否缺少 using 指令或程序集引用?)

        可以通过以下方式解决此问题:

      • 创建新的 .NET Core 类库项目。
      • 在新项目中,添加引用以.proto 文件启用 C# 代码生成
        • 添加以下包引用:
          • Grpc.Tools
          • Grpc.Net.Client
          • Google.Protobuf
          • .proto 文件添加到 <Protobuf> 项目组。
          • 在 WPF 应用程序中,添加对新项目的引用。
          • WPF 应用程序可以使用来自新类库项目的 gRPC 生成的类型。

            调用子目录中托管的 gRPC 服务

            许多第三方 gRPC 工具不支持子目录中托管的服务。 请考虑找到将 gRPC 托管为根目录的方法。

            发出 gRPC 调用时,将忽略 gRPC 通道地址的路径部分。 例如,当路由对服务的 gRPC 调用时,GrpcChannel.ForAddress("https://localhost:5001/ignored_path") 不使用 ignored_path

            由于 gRPC 具有标准化的规范地址结构,因此会忽略地址路径。 gRPC 地址组合了包、服务和方法的名称:https://localhost:5001/PackageName.ServiceName/MethodName

            在某些情况下,应用需要包含具有 gRPC 调用的路径。 例如,当 ASP.NET Core gRPC 应用托管在 IIS 目录中时,需要在请求中包含该目录。 如果路径是必需的,则可以使用下面指定的自定义 SubdirectoryHandler 方法将其添加到 gRPC 调用:

            /// <summary>
            /// A delegating handler that adds a subdirectory to the URI of gRPC requests.
            /// </summary>
            public class SubdirectoryHandler : DelegatingHandler
                private readonly string _subdirectory;
                public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
                    : base(innerHandler)
                    _subdirectory = subdirectory;
                protected override Task<HttpResponseMessage> SendAsync(
                    HttpRequestMessage request, CancellationToken cancellationToken)
                    var old = request.RequestUri;
                    var url = $"{old.Scheme}://{old.Host}:{old.Port}";
                    url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
                    request.RequestUri = new Uri(url, UriKind.Absolute);
                    return base.SendAsync(request, cancellationToken);
            

            SubdirectoryHandler 在创建 gRPC 通道时使用。

            var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");
            var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
            var client = new Greet.GreeterClient(channel);
            var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });
            

            前面的代码:

          • 创建具有 /MyApp 路径的 SubdirectoryHandler
          • 配置通道以使用 SubdirectoryHandler
          • 使用 SayHelloAsync 调用 gRPC 服务。 gRPC 调用将发送到 https://localhost:5001/MyApp/greet.Greeter/SayHello
          • 或者,可以使用 AddHttpMessageHandler 为客户端工厂配置 SubdirectoryHandler

            将 gRPC 客户端配置为使用 HTTP/3

            .NET gRPC 客户端支持使用 .NET 6 或更高版本的 HTTP/3。 如果服务器向客户端发送表明服务器支持 HTTP/3 的 alt-svc 响应标头,则客户端将自动将其连接升级到 HTTP/3。 有关如何在服务器上启用 HTTP/3 的信息,请参阅将 HTTP/3 与 ASP.NET Core Kestrel Web 服务器结合使用

            默认情况下,.NET 8 中的 HTTP/3 支持处于启用状态。 .NET 6 和 .NET 7 中的 HTTP/3 支持需要通过项目文件中的一个配置标志启用:

            <ItemGroup>
              <RuntimeHostConfigurationOption Include="System.Net.SocketsHttpHandler.Http3Support" Value="true" />
            </ItemGroup>
            

            也可以使用 AppContext.SetSwitch 设置 System.Net.SocketsHttpHandler.Http3Support

            DelegatingHandler 可用于强制 gRPC 客户端使用 HTTP/3。 强制使用 HTTP/3 可以避免升级请求的开销。 使用类似于下面的代码强制使用 HTTP/3:

            /// <summary>
            /// A delegating handler that changes the request HTTP version to HTTP/3.
            /// </summary>
            public class Http3Handler : DelegatingHandler
                public Http3Handler() { }
                public Http3Handler(HttpMessageHandler innerHandler) : base(innerHandler) { }
                protected override Task<HttpResponseMessage> SendAsync(
                    HttpRequestMessage request, CancellationToken cancellationToken)
                    request.Version = HttpVersion.Version30;
                    request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;
                    return base.SendAsync(request, cancellationToken);
            

            Http3Handler 在创建 gRPC 通道时使用。 以下代码创建了一个配置为使用 Http3Handler 的通道。

            var handler = new Http3Handler(new HttpClientHandler());
            var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
            var client = new Greet.GreeterClient(channel);
            var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });
            

            或者,可以使用 AddHttpMessageHandler 为客户端工厂配置 Http3Handler

            在 Alpine Linux 上构建 gRPC

            Grpc.Tools 包使用称为 protoc 的捆绑的本机二进制文件.proto 文件生成 .NET 类型。 在 Grpc.Tools 中的本机二进制文件(如 Alpine Linux)不支持的平台上构建 gRPC 应用还需执行其他步骤。

            提前生成代码

            一种解决方案可提前生成代码。

          • .proto 文件和 Grpc.Tools 包引用移动到新项目。
          • 将项目作为 NuGet 包发布,然后将其上传到 NuGet 源。
          • 更新应用以引用 NuGet 包。
          • 通过上述步骤,应用不再需要 Grpc.Tools 来构建,因为代码可提前生成。

            自定义 Grpc.Tools 本机二进制文件

            Grpc.Tools 支持使用自定义本机二进制文件。 此功能允许 gRPC 工具在其捆绑的本机二进制文件不支持的环境中运行。

            生成或获取 protocgrpc_csharp_plugin 本机二进制文件,并配置 Grpc.Tools 以使用它们。 通过设置以下环境变量来配置本机二进制文件:

          • PROTOBUF_PROTOC - 协议缓冲区编译器的完整路径
          • GRPC_PROTOC_PLUGIN - grpc_csharp_plugin 的完整路径
          • 对于 Alpine Linux,https://pkgs.alpinelinux.org/ 上有社区提供的用于协议缓冲区编译器和 gRPC 插件的包。

            # Build or install the binaries for your architecture.
            # e.g. for Alpine Linux the grpc-plugins package can be used
            #  See https://pkgs.alpinelinux.org/package/edge/community/x86_64/grpc-plugins
            apk add grpc-plugins  # Alpine Linux specific package installer
            # Set environment variables for the built/installed protoc
            # and grpc_csharp_plugin binaries
            export PROTOBUF_PROTOC=/usr/bin/protoc
            export GRPC_PROTOC_PLUGIN=/usr/bin/grpc_csharp_plugin
            # When dotnet build runs, the Grpc.Tools NuGet package
            # uses the binaries pointed to by the environment variables.
            dotnet build
            

            有关将 Grpc.Tools 与不受支持的体系结构配合使用的详细信息,请参阅 gRPC 构建集成文档

            本文档讨论了在 .NET 上开发 gRPC 应用时经常遇到的问题。

            客户端和服务 SSL/TLS 配置不匹配

            默认情况下,gRPC 模板和示例使用传输层安全性 (TLS) 来保护 gRPC 服务。 gRPC 客户端需要使用安全连接才能成功调用受保护的 gRPC 服务。

            可在应用启动时验证 ASP.NET Core gRPC 服务是否正在使用 TLS。 该服务将在 HTTPS 终结点上侦听:

            info: Microsoft.Hosting.Lifetime[0]
                  Now listening on: https://localhost:5001
            info: Microsoft.Hosting.Lifetime[0]
                  Application started. Press Ctrl+C to shut down.
            info: Microsoft.Hosting.Lifetime[0]
                  Hosting environment: Development
            

            .NET Core 客户端必须在服务器地址中使用 https 才能使用安全连接进行调用:

            static async Task Main(string[] args)
                // The port number(5001) must match the port of the gRPC server.
                var channel = GrpcChannel.ForAddress("https://localhost:5001");
                var client = new Greet.GreeterClient(channel);
            

            所有 gRPC 客户端实现都支持 TLS。 其他语言的 gRPC 客户端通常需要配置有 SslCredentials 的通道。 SslCredentials 指定客户端将使用的证书,必须使用该证书,而不是使用不安全凭据。 有关配置不同 gRPC 客户端实现以使用 TLS 的示例,请参阅 gRPC 身份验证

            使用不受信任/无效证书调用 gRPC 服务

            .NET gRPC 客户端要求服务具有受信任的证书。 在没有受信任证书的情况下调用 gRPC 服务时,将返回以下错误消息:

            未经处理的异常。 System.Net.Http.HttpRequestException:无法建立 SSL 连接,请查看内部异常。 ---> System.Security.Authentication.AuthenticationException:根据验证过程,远程证书无效。

            如果在本地测试应用且 ASP.NET Core HTTPS 开发证书不受信任,则可能会显示此错误。 有关解决此问题的说明,请参阅在 Windows 和 macOS 上信任 ASP.NET Core HTTPS 开发证书

            如果要在另一台计算机上调用 gRPC 服务且无法信任该证书,则可以将 gRPC 客户端配置为忽略无效的证书。 下面的代码使用 HttpClientHandler.ServerCertificateCustomValidationCallback 来允许在没有受信任证书的情况下进行调用:

            var handler = new HttpClientHandler();
            handler.ServerCertificateCustomValidationCallback = 
                HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
            var channel = GrpcChannel.ForAddress("https://localhost:5001",
                new GrpcChannelOptions { HttpHandler = handler });
            var client = new Greet.GreeterClient(channel);
            

            gRPC 客户端工厂允许在没有受信任证书的情况下调用。 使用 ConfigurePrimaryHttpMessageHandler 扩展方法在客户端上配置处理程序:

            services
                .AddGrpcClient<Greeter.GreeterClient>(o =>
                    o.Address = new Uri("https://localhost:5001");
                .ConfigurePrimaryHttpMessageHandler(() =>
                    var handler = new HttpClientHandler();
                    handler.ServerCertificateCustomValidationCallback = 
                        HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
                    return handler;
            

            不受信任的证书只应在应用开发过程中使用。 生产应用应始终使用有效的证书。

            使用 .NET Core 客户端调用不安全的 gRPC 服务

            .NET gRPC 可以通过在服务器地址中指定 http 来调用不安全的 gRPC 服务。 例如,GrpcChannel.ForAddress("http://localhost:5000")

            根据应用使用的 .NET 版本,调用不安全的 gRPC 服务还需要满足一些额外要求:

          • .NET 5 或更高版本要求使用 Grpc.Net.Client 版本 2.32.0 或更高版本。

          • .NET Core 3.x 要求进行额外配置。 应用必须将 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 开关设置为 true

            // This switch must be set before creating the GrpcChannel/HttpClient.
            AppContext.SetSwitch(
                "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
            // The port number(5000) must match the port of the gRPC server.
            var channel = GrpcChannel.ForAddress("http://localhost:5000");
            var client = new Greet.GreeterClient(channel);
            

            只有 .NET Core 3.x 需要 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 开关。 .NET 5 中不需要任何额外配置,也没有这项要求。

            必须在仅 HTTP/2 端口上托管不安全的 gRPC 服务。 有关详细信息,请参阅 ASP.NET Core 协议协商

            无法在 macOS 上启动 ASP.NET Core gRPC 应用

            Kestrel 不支持在使用 .NET 8 之前版本的 macOS 上使用带 TLS 的 HTTP/2。 默认情况下,ASP.NET Core gRPC 模板和示例使用 TLS。 尝试启动 gRPC 服务器时,你将看到以下错误消息:

            无法绑定到 IPv4 环回接口上的 https://localhost:5001 :“由于缺少 ALPN 支持,macOS 不支持使用 TLS 的 HTTP/2。”。

            若要解决 .NET 7 及更早版本中的此问题,请将 Kestrel 和 gRPC 客户端配置为使用不带有 TLS 的 HTTP/2。 应仅在开发过程中执行此操作。 如果不使用 TLS,将会在不加密的情况下发送 gRPC 消息。

            Kestrel 必须在 Program.cs 中配置一个不带 TLS 的 HTTP/2 终结点:

            public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                    .ConfigureWebHostDefaults(webBuilder =>
                        webBuilder.ConfigureKestrel(options =>
                            // Setup a HTTP/2 endpoint without TLS.
                            options.ListenLocalhost(5000, o => o.Protocols = 
                                HttpProtocols.Http2);
                        webBuilder.UseStartup<Startup>();
            

            如果在未带有 TLS 的情况下配置了 HTTP/2 终结点,则终结点的 ListenOptions.Protocols 必须设置为 HttpProtocols.Http2。 无法使用 HttpProtocols.Http1AndHttp2,因为需要使用 TLS 来协商 HTTP/2。 如果未带有 TLS,则与终结点的所有连接均默认为 HTTP/1.1,且 gRPC 调用会失败。

            还必须将 gRPC 客户端配置为不使用 TLS。 有关详细信息,请参阅使用 .NET Core 客户端调用不安全的 gRPC 服务

            应仅在应用开发过程中使用不带有 TLS 的 HTTP/2。 生产应用应始终使用传输安全性。 有关详细信息,请参阅适用于 ASP.NET Core 的 gRPC 的安全注意事项

            gRPC C# 资产不是从 .proto 文件生成的代码

            具体客户端和服务基类的 gRPC 代码生成需要从项目引用 protobuf 文件和工具。 必须包括:

          • 要在 <Protobuf> 项目组中使用的 .proto 文件。 导入的 .proto 文件必须由项目引用。
          • 对 gRPC 工具包 Grpc.Tools 的包引用。
          • 有关生成 gRPC C# 资产的详细信息,请参阅使用 C# 的 gRPC 服务

            托管 gRPC 服务的 ASP.NET Core web 应用仅需要已生成的服务基类:

            <ItemGroup>
              <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
            </ItemGroup>
            

            发出 gRPC 调用的 gRPC 客户端应用仅需要已生成的具体客户端:

            <ItemGroup>
              <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
            </ItemGroup>
            

            WPF 项目无法从 .proto 文件生成 gRPC C# 资产

            WPF 项目存在一个已知问题,其阻止 gRPC 代码生成正常运行。 在 WPF 项目中通过引用 Grpc.Tools.proto 文件生成的任何 gRPC 类型在使用时都将创建编译错误:

            错误 CS0246:找不到类型名称或命名空间名称 ’MyGrpcServices’ (是否缺少 using 指令或程序集引用?)

            可以通过以下方式解决此问题:

          • 创建新的 .NET Core 类库项目。
          • 在新项目中,添加引用以.proto 文件启用 C# 代码生成
            • 添加以下包引用:
              • Grpc.Tools
              • Grpc.Net.Client
              • Google.Protobuf
              • .proto 文件添加到 <Protobuf> 项目组。
              • 在 WPF 应用程序中,添加对新项目的引用。
              • WPF 应用程序可以使用来自新类库项目的 gRPC 生成的类型。

                调用子目录中托管的 gRPC 服务

                许多第三方 gRPC 工具不支持子目录中托管的服务。 请考虑找到将 gRPC 托管为根目录的方法。

                发出 gRPC 调用时,将忽略 gRPC 通道地址的路径部分。 例如,当路由对服务的 gRPC 调用时,GrpcChannel.ForAddress("https://localhost:5001/ignored_path") 不使用 ignored_path

                由于 gRPC 具有标准化的规范地址结构,因此会忽略地址路径。 gRPC 地址组合了包、服务和方法的名称:https://localhost:5001/PackageName.ServiceName/MethodName

                在某些情况下,应用需要包含具有 gRPC 调用的路径。 例如,当 ASP.NET Core gRPC 应用托管在 IIS 目录中时,需要在请求中包含该目录。 如果路径是必需的,则可以使用下面指定的自定义 SubdirectoryHandler 方法将其添加到 gRPC 调用:

                /// <summary>
                /// A delegating handler that adds a subdirectory to the URI of gRPC requests.
                /// </summary>
                public class SubdirectoryHandler : DelegatingHandler
                    private readonly string _subdirectory;
                    public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
                        : base(innerHandler)
                        _subdirectory = subdirectory;
                    protected override Task<HttpResponseMessage> SendAsync(
                        HttpRequestMessage request, CancellationToken cancellationToken)
                        var old = request.RequestUri;
                        var url = $"{old.Scheme}://{old.Host}:{old.Port}";
                        url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
                        request.RequestUri = new Uri(url, UriKind.Absolute);
                        return base.SendAsync(request, cancellationToken);
                

                SubdirectoryHandler 在创建 gRPC 通道时使用。

                var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");
                var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
                var client = new Greet.GreeterClient(channel);
                var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });
                

                前面的代码:

              • 创建具有 /MyApp 路径的 SubdirectoryHandler
              • 配置通道以使用 SubdirectoryHandler
              • 使用 SayHelloAsync 调用 gRPC 服务。 gRPC 调用将发送到 https://localhost:5001/MyApp/greet.Greeter/SayHello
              • 或者,可以使用 AddHttpMessageHandler 为客户端工厂配置 SubdirectoryHandler

                本文档讨论了在 .NET 上开发 gRPC 应用时经常遇到的问题。

                客户端和服务 SSL/TLS 配置不匹配

                默认情况下,gRPC 模板和示例使用传输层安全性 (TLS) 来保护 gRPC 服务。 gRPC 客户端需要使用安全连接才能成功调用受保护的 gRPC 服务。

                可在应用启动时验证 ASP.NET Core gRPC 服务是否正在使用 TLS。 该服务将在 HTTPS 终结点上侦听:

                info: Microsoft.Hosting.Lifetime[0]
                      Now listening on: https://localhost:5001
                info: Microsoft.Hosting.Lifetime[0]
                      Application started. Press Ctrl+C to shut down.
                info: Microsoft.Hosting.Lifetime[0]
                      Hosting environment: Development
                

                .NET Core 客户端必须在服务器地址中使用 https 才能使用安全连接进行调用:

                static async Task Main(string[] args)
                    // The port number(5001) must match the port of the gRPC server.
                    var channel = GrpcChannel.ForAddress("https://localhost:5001");
                    var client = new Greet.GreeterClient(channel);
                

                所有 gRPC 客户端实现都支持 TLS。 其他语言的 gRPC 客户端通常需要配置有 SslCredentials 的通道。 SslCredentials 指定客户端将使用的证书,必须使用该证书,而不是使用不安全凭据。 有关配置不同 gRPC 客户端实现以使用 TLS 的示例,请参阅 gRPC 身份验证

                使用不受信任/无效证书调用 gRPC 服务

                .NET gRPC 客户端要求服务具有受信任的证书。 在没有受信任证书的情况下调用 gRPC 服务时,将返回以下错误消息:

                未经处理的异常。 System.Net.Http.HttpRequestException:无法建立 SSL 连接,请查看内部异常。 ---> System.Security.Authentication.AuthenticationException:根据验证过程,远程证书无效。

                如果在本地测试应用且 ASP.NET Core HTTPS 开发证书不受信任,则可能会显示此错误。 有关解决此问题的说明,请参阅在 Windows 和 macOS 上信任 ASP.NET Core HTTPS 开发证书

                如果要在另一台计算机上调用 gRPC 服务且无法信任该证书,则可以将 gRPC 客户端配置为忽略无效的证书。 下面的代码使用 HttpClientHandler.ServerCertificateCustomValidationCallback 来允许在没有受信任证书的情况下进行调用:

                var handler = new HttpClientHandler();
                handler.ServerCertificateCustomValidationCallback = 
                    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
                var channel = GrpcChannel.ForAddress("https://localhost:5001",
                    new GrpcChannelOptions { HttpHandler = handler });
                var client = new Greet.GreeterClient(channel);
                

                gRPC 客户端工厂允许在没有受信任证书的情况下调用。 使用 ConfigurePrimaryHttpMessageHandler 扩展方法在客户端上配置处理程序:

                services
                    .AddGrpcClient<Greeter.GreeterClient>(o =>
                        o.Address = new Uri("https://localhost:5001");
                    .ConfigurePrimaryHttpMessageHandler(() =>
                        var handler = new HttpClientHandler();
                        handler.ServerCertificateCustomValidationCallback = 
                            HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
                        return handler;
                

                不受信任的证书只应在应用开发过程中使用。 生产应用应始终使用有效的证书。

                使用 .NET Core 客户端调用不安全的 gRPC 服务

                .NET gRPC 可以通过在服务器地址中指定 http 来调用不安全的 gRPC 服务。 例如,GrpcChannel.ForAddress("http://localhost:5000")

                根据应用使用的 .NET 版本,调用不安全的 gRPC 服务还需要满足一些额外要求:

              • .NET 5 或更高版本要求使用 Grpc.Net.Client 版本 2.32.0 或更高版本。

              • .NET Core 3.x 要求进行额外配置。 应用必须将 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 开关设置为 true

                // This switch must be set before creating the GrpcChannel/HttpClient.
                AppContext.SetSwitch(
                    "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
                // The port number(5000) must match the port of the gRPC server.
                var channel = GrpcChannel.ForAddress("http://localhost:5000");
                var client = new Greet.GreeterClient(channel);
                

                只有 .NET Core 3.x 需要 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 开关。 .NET 5 中不需要任何额外配置,也没有这项要求。

                必须在仅 HTTP/2 端口上托管不安全的 gRPC 服务。 有关详细信息,请参阅 ASP.NET Core 协议协商

                无法在 macOS 上启动 ASP.NET Core gRPC 应用

                Kestrel 不支持在使用 .NET 8 之前版本的 macOS 上使用带 TLS 的 HTTP/2。 默认情况下,ASP.NET Core gRPC 模板和示例使用 TLS。 尝试启动 gRPC 服务器时,你将看到以下错误消息:

                无法绑定到 IPv4 环回接口上的 https://localhost:5001 :“由于缺少 ALPN 支持,macOS 不支持使用 TLS 的 HTTP/2。”。

                若要解决 .NET 7 及更早版本中的此问题,请将 Kestrel 和 gRPC 客户端配置为使用不带有 TLS 的 HTTP/2。 应仅在开发过程中执行此操作。 如果不使用 TLS,将会在不加密的情况下发送 gRPC 消息。

                Kestrel 必须在 Program.cs 中配置一个不带 TLS 的 HTTP/2 终结点:

                public static IHostBuilder CreateHostBuilder(string[] args) =>
                    Host.CreateDefaultBuilder(args)
                        .ConfigureWebHostDefaults(webBuilder =>
                            webBuilder.ConfigureKestrel(options =>
                                // Setup a HTTP/2 endpoint without TLS.
                                options.ListenLocalhost(5000, o => o.Protocols = 
                                    HttpProtocols.Http2);
                            webBuilder.UseStartup<Startup>();
                

                如果在未带有 TLS 的情况下配置了 HTTP/2 终结点,则终结点的 ListenOptions.Protocols 必须设置为 HttpProtocols.Http2。 无法使用 HttpProtocols.Http1AndHttp2,因为需要使用 TLS 来协商 HTTP/2。 如果未带有 TLS,则与终结点的所有连接均默认为 HTTP/1.1,且 gRPC 调用会失败。

                还必须将 gRPC 客户端配置为不使用 TLS。 有关详细信息,请参阅使用 .NET Core 客户端调用不安全的 gRPC 服务

                应仅在应用开发过程中使用不带有 TLS 的 HTTP/2。 生产应用应始终使用传输安全性。 有关详细信息,请参阅适用于 ASP.NET Core 的 gRPC 的安全注意事项

                gRPC C# 资产不是从 .proto 文件生成的代码

                具体客户端和服务基类的 gRPC 代码生成需要从项目引用 protobuf 文件和工具。 必须包括:

              • 要在 <Protobuf> 项目组中使用的 .proto 文件。 导入的 .proto 文件必须由项目引用。
              • 对 gRPC 工具包 Grpc.Tools 的包引用。
              • 有关生成 gRPC C# 资产的详细信息,请参阅使用 C# 的 gRPC 服务

                托管 gRPC 服务的 ASP.NET Core web 应用仅需要已生成的服务基类:

                <ItemGroup>
                  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
                </ItemGroup>
                

                发出 gRPC 调用的 gRPC 客户端应用仅需要已生成的具体客户端:

                <ItemGroup>
                  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
                </ItemGroup>
                

                WPF 项目无法从 .proto 文件生成 gRPC C# 资产

                WPF 项目存在一个已知问题,其阻止 gRPC 代码生成正常运行。 在 WPF 项目中通过引用 Grpc.Tools.proto 文件生成的任何 gRPC 类型在使用时都将创建编译错误:

                错误 CS0246:找不到类型名称或命名空间名称 ’MyGrpcServices’ (是否缺少 using 指令或程序集引用?)

                可以通过以下方式解决此问题:

              • 创建新的 .NET Core 类库项目。
              • 在新项目中,添加引用以.proto 文件启用 C# 代码生成
                • 添加以下包引用:
                  • Grpc.Tools
                  • Grpc.Net.Client
                  • Google.Protobuf
                  • .proto 文件添加到 <Protobuf> 项目组。
                  • 在 WPF 应用程序中,添加对新项目的引用。
                  • WPF 应用程序可以使用来自新类库项目的 gRPC 生成的类型。

                    调用子目录中托管的 gRPC 服务

                    许多第三方 gRPC 工具不支持子目录中托管的服务。 请考虑找到将 gRPC 托管为根目录的方法。

                    发出 gRPC 调用时,将忽略 gRPC 通道地址的路径部分。 例如,当路由对服务的 gRPC 调用时,GrpcChannel.ForAddress("https://localhost:5001/ignored_path") 不使用 ignored_path

                    由于 gRPC 具有标准化的规范地址结构,因此会忽略地址路径。 gRPC 地址组合了包、服务和方法的名称:https://localhost:5001/PackageName.ServiceName/MethodName

                    在某些情况下,应用需要包含具有 gRPC 调用的路径。 例如,当 ASP.NET Core gRPC 应用托管在 IIS 目录中时,需要在请求中包含该目录。 如果路径是必需的,则可以使用下面指定的自定义 SubdirectoryHandler 方法将其添加到 gRPC 调用:

                    /// <summary>
                    /// A delegating handler that adds a subdirectory to the URI of gRPC requests.
                    /// </summary>
                    public class SubdirectoryHandler : DelegatingHandler
                        private readonly string _subdirectory;
                        public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
                            : base(innerHandler)
                            _subdirectory = subdirectory;
                        protected override Task<HttpResponseMessage> SendAsync(
                            HttpRequestMessage request, CancellationToken cancellationToken)
                            var old = request.RequestUri;
                            var url = $"{old.Scheme}://{old.Host}:{old.Port}";
                            url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
                            request.RequestUri = new Uri(url, UriKind.Absolute);
                            return base.SendAsync(request, cancellationToken);
                    

                    SubdirectoryHandler 在创建 gRPC 通道时使用。

                    var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");
                    var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
                    var client = new Greet.GreeterClient(channel);
                    var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });
                    

                    前面的代码:

                  • 创建具有 /MyApp 路径的 SubdirectoryHandler
                  • 配置通道以使用 SubdirectoryHandler
                  • 使用 SayHelloAsync 调用 gRPC 服务。 gRPC 调用将发送到 https://localhost:5001/MyApp/greet.Greeter/SayHello
                  • 或者,可以使用 AddHttpMessageHandler 为客户端工厂配置 SubdirectoryHandler

  •