.NET での gRPC のトラブルシューティング

作成者: James Newton-King

注意

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

警告

このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、「.NET および .NET Core サポート ポリシー」を参照してください。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

このドキュメントでは、.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 サービスの呼び出しには、さらに他の要件もいくつかあります。

重要

セキュリティで保護されていない gRPC サービスは、HTTP/2 専用ポートでホストする必要があります。 詳細については、ASP.NET Core プロトコルのネゴシエーションに関する記事を参照してください。

MacOS で ASP.NET Core gRPC アプリを起動できない

Kestrel では、.NET 8 より前の macOS での TLS を使用する HTTP/2 はサポートされていません。 ASP.NET Core gRPC テンプレートとサンプルでは、既定で TLS を使用しています。 gRPC サーバーを起動しようとすると、次のエラー メッセージが表示されます。

Unable to bind to https://localhost:5001 on the IPv4 loopback interface:'HTTP/2 over TLS is not supported on macOS due to missing ALPN support.'. (IPv4 ループバック インターフェイスで https://localhost:5001 にバインドできません。'HTTP/2 over TLS は ALPN サポートがないため、macOS でサポートされていません。')

.NET 7 以前でこの問題を回避するには、TLS を "使わずに" HTTP/2 を使用するように Kestrel と gRPC クライアントを構成します。 これは開発時にのみ実行してください。 TLS を使用しないと、gRPC メッセージが暗号化されずに送信されます。 詳細については、Asp.Net Core 7.0: macOS で ASP.NET Core gRPC アプリを起動できない場合に関するページを参照してください。

gRPC C# アセットが .proto ファイルから生成されたコードでない

具象クライアントとサービス基本クラスの gRPC コード生成には、protobuf ファイルとツールをプロジェクトから参照する必要があります。 次のものを含める必要があります。

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:型または名前空間の名前 'tMyGrpcServices' が見つかりませんでした (using ディレクティブまたはアセンブリ参照が指定されていることを確認してください)

この問題は次の方法で回避できます。

  1. 新しい .NET Core クラス ライブラリ プロジェクトを作成します。
  2. 新しいプロジェクトで、参照を追加して、.proto ファイルからの C# コード生成を有効にします。
  3. WPF アプリケーションで、新しいプロジェクトに参照を追加します。

WPF アプリケーションでは、新しいクラス ライブラリ プロジェクトから、gRPC によって生成された型を使用できます。

サブディレクトリでホストされている gRPC サービスの呼び出し

警告

多くのサードパーティ製 gRPC ツールでは、サブディレクトリでホストされているサービスはサポートされていません。 ルート ディレクトリとして gRPC をホストする方法を見つけることを検討してください。

gRPC 呼び出しを行うときに、gRPC チャネルのアドレスのパス コンポーネントは無視されます。 たとえば、GrpcChannel.ForAddress("https://localhost:5001/ignored_path") では、サービスの gRPC 呼び出しをルーティングするときに 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 に送信されます。

クライアント ファクトリは、SubdirectoryHandlerAddHttpMessageHandler を使用して構成することもできます。

HTTP/3 を使用するように gRPC クライアントを構成する

.NET gRPC クライアントは、.NET 6 以降で HTTP/3 をサポートします。 HTTP/3 がサポートされることを示す alt-svc 応答ヘッダーがサーバーからクライアントに送信された場合、クライアントは自動的にその接続を HTTP/3 にアップグレードします。 詳しくは、「ASP.NET Core Kestrel Web サーバーで HTTP/3 を使用する」を参照してください。

DelegatingHandler を使用すると、HTTP/3 の使用を gRPC クライアントに強制することができます。 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" });

クライアント ファクトリは、Http3HandlerAddHttpMessageHandler を使用して構成することもできます。

Alpine Linux での gRPC の構築

Grpc.Tools パッケージは、protoc というバンドルされたネイティブ バイナリを使って .proto ファイルから .NET 型を生成します。 Alpine Linux のように Grpc.Tools のネイティブ バイナリがサポートしないプラットフォーム上で gRPC アプリを構築するには、追加の手順が必要です。

事前にコードを生成する

解決策の 1 つは、事前にコードを生成することです。

  1. .proto ファイルと Grpc.Tools パッケージ参照を新しいプロジェクトに移動します。
  2. プロジェクトを NuGet パッケージとして発行し、NuGet フィードにアップロードします。
  3. NuGet パッケージを参照するようにアプリを更新します。

先ほどの手順でコードは事前に生成されるため、アプリのビルドに Grpc.Tools は必要なくなりました。

Grpc.Tools ネイティブ バイナリをカスタマイズする

Grpc.Tools はカスタム ネイティブ バイナリの使用をサポートしています。 この機能により、バンドルされたネイティブ バイナリがサポートしていない環境でも gRPC ツールを実行できます。

protocgrpc_csharp_plugin のネイティブ バイナリをビルドするか入手し、それらを使うように Grpc.Tools を構成します。 次の環境変数を設定してネイティブ バイナリを構成します。

  • PROTOBUF_PROTOC - プロトコル バッファー コンパイラの完全なパス
  • GRPC_PROTOC_PLUGIN - grpc_csharp_plugin の完全なパス

Alpine Linux の場合、コミュニティが提供しているプロトコル バッファー コンパイラと gRPC プラグイン用パッケージが https://pkgs.alpinelinux.org/ に掲載されています。

# Build or install the binaries for your architecture.

# 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 ビルド統合ドキュメントを参照してください。

HttpClient.Timeout からの gRPC 呼び出しタイムアウト

HttpClient は、既定で 100 秒のタイムアウトが構成されています。 GrpcChannelHttpClient を使用するように構成されている場合、実行時間の長い gRPC ストリーミング呼び出しは、タイムアウト制限内に完了しない場合は取り消されます。

System.OperationCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.

このエラーを修正するには、いくつかの方法があります。 1 つ目は、HttpClient.Timeout を大きな値に構成することです。 Timeout.InfiniteTimeSpan はタイムアウトを無効にします。

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var httpClient = new HttpClient(handler) { Timeout = Timeout.InfiniteTimeSpan };
var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpClient = httpClient });
var client = new Greeter.GreeterClient(channel);

または、HttpClient を作成せずに、代わりに GrpcChannel.HttpHandler を設定します。

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);

このドキュメントでは、.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);
    

System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport スイッチは、.NET Core 3.x. でのみ必要です。 .NET 5 では、何も実行されないため、必要ではありません。

重要

セキュリティで保護されていない gRPC サービスは、HTTP/2 専用ポートでホストする必要があります。 詳細については、ASP.NET Core プロトコルのネゴシエーションに関する記事を参照してください。

MacOS で ASP.NET Core gRPC アプリを起動できない

Kestrel では、.NET 8 より前の macOS での TLS を使用する HTTP/2 はサポートされていません。 ASP.NET Core gRPC テンプレートとサンプルでは、既定で TLS を使用しています。 gRPC サーバーを起動しようとすると、次のエラー メッセージが表示されます。

Unable to bind to https://localhost:5001 on the IPv4 loopback interface:'HTTP/2 over TLS is not supported on macOS due to missing ALPN support.'. (IPv4 ループバック インターフェイスで https://localhost:5001 にバインドできません。'HTTP/2 over TLS は ALPN サポートがないため、macOS でサポートされていません。')

.NET 7 以前でこの問題を回避するには、TLS を "使わずに" HTTP/2 を使用するように Kestrel と gRPC クライアントを構成します。 これは開発時にのみ実行してください。 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);
});
  • 上記のコードでは、ローカルホスト ポート番号 5287 を gRPC サービス プロジェクト内の Properties/launchSettings.json で指定された HTTP (HTTPS ではない) ポート番号に置き換えます。

HTTP/2 エンドポイントが TLS を使用せずに構成されている場合、エンドポイントの ListenOptions.ProtocolHttpProtocols.Http2 に設定する必要があります。 HTTP/2 のネゴシエートに TLS が必要であるため、HttpProtocols.Http1AndHttp2 は使用できません。 TLS を使用しない場合、エンドポイントへのすべての接続が既定で HTTP/1.1 に設定され、gRPC の呼び出しが失敗します。

GRPC クライアントでも、TLS を使用しないように構成する必要があります。 詳細については、「.NET Core クライアントで安全でない gRPC サービスを呼び出す」を参照してください。

警告

TLS を使用しない HTTP/2 は、アプリの開発時にのみ使用してください。 実稼働アプリでは、常にトランスポート セキュリティを使用する必要があります。 詳細については、 gRPC for ASP.NET Core のセキュリティの考慮事項 に関するページを参照してください。

gRPC C# アセットが .proto ファイルから生成されたコードでない

具象クライアントとサービス基本クラスの gRPC コード生成には、protobuf ファイルとツールをプロジェクトから参照する必要があります。 次のものを含める必要があります。

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:型または名前空間の名前 'tMyGrpcServices' が見つかりませんでした (using ディレクティブまたはアセンブリ参照が指定されていることを確認してください)

この問題は次の方法で回避できます。

  1. 新しい .NET Core クラス ライブラリ プロジェクトを作成します。
  2. 新しいプロジェクトで、参照を追加して、.proto ファイルからの C# コード生成を有効にします。
  3. WPF アプリケーションで、新しいプロジェクトに参照を追加します。

WPF アプリケーションでは、新しいクラス ライブラリ プロジェクトから、gRPC によって生成された型を使用できます。

サブディレクトリでホストされている gRPC サービスの呼び出し

警告

多くのサードパーティ製 gRPC ツールでは、サブディレクトリでホストされているサービスはサポートされていません。 ルート ディレクトリとして gRPC をホストする方法を見つけることを検討してください。

gRPC 呼び出しを行うときに、gRPC チャネルのアドレスのパス コンポーネントは無視されます。 たとえば、GrpcChannel.ForAddress("https://localhost:5001/ignored_path") では、サービスの gRPC 呼び出しをルーティングするときに 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 に送信されます。

クライアント ファクトリは、SubdirectoryHandlerAddHttpMessageHandler を使用して構成することもできます。

HTTP/3 を使用するように gRPC クライアントを構成する

.NET gRPC クライアントは、.NET 6 以降で HTTP/3 をサポートします。 HTTP/3 がサポートされることを示す alt-svc 応答ヘッダーがサーバーからクライアントに送信された場合、クライアントは自動的にその接続を HTTP/3 にアップグレードします。 サーバー上で HTTP/3 を有効にする方法については、「ASP.NET Core Kestrel Web サーバーで HTTP/3 を使用する」を参照してください。

.NET 8 では HTTP/3 のサポートが既定で有効になっています。 .NET 6 と .NET 7 での HTTP/3 のサポートは、プロジェクト ファイルの構成フラグを使って有効にする必要があります。

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

System.Net.SocketsHttpHandler.Http3Support は、AppContext.SetSwitch を使用して設定することもできます。

DelegatingHandler を使用すると、HTTP/3 の使用を gRPC クライアントに強制することができます。 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" });

クライアント ファクトリは、Http3HandlerAddHttpMessageHandler を使用して構成することもできます。

Alpine Linux での gRPC の構築

Grpc.Tools パッケージは、protoc というバンドルされたネイティブ バイナリを使って .proto ファイルから .NET 型を生成します。 Alpine Linux のように Grpc.Tools のネイティブ バイナリがサポートしないプラットフォーム上で gRPC アプリを構築するには、追加の手順が必要です。

事前にコードを生成する

解決策の 1 つは、事前にコードを生成することです。

  1. .proto ファイルと Grpc.Tools パッケージ参照を新しいプロジェクトに移動します。
  2. プロジェクトを NuGet パッケージとして発行し、NuGet フィードにアップロードします。
  3. NuGet パッケージを参照するようにアプリを更新します。

先ほどの手順でコードは事前に生成されるため、アプリのビルドに Grpc.Tools は必要なくなりました。

Grpc.Tools ネイティブ バイナリをカスタマイズする

Grpc.Tools はカスタム ネイティブ バイナリの使用をサポートしています。 この機能により、バンドルされたネイティブ バイナリがサポートしていない環境でも gRPC ツールを実行できます。

protocgrpc_csharp_plugin のネイティブ バイナリをビルドするか入手し、それらを使うように Grpc.Tools を構成します。 次の環境変数を設定してネイティブ バイナリを構成します。

  • PROTOBUF_PROTOC - プロトコル バッファー コンパイラの完全なパス
  • GRPC_PROTOC_PLUGIN - grpc_csharp_plugin の完全なパス

Alpine Linux の場合、コミュニティが提供しているプロトコル バッファー コンパイラと gRPC プラグイン用パッケージが https://pkgs.alpinelinux.org/ に掲載されています。

# Build or install the binaries for your architecture.

# 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 ビルド統合ドキュメントを参照してください。

HttpClient.Timeout からの gRPC 呼び出しタイムアウト

HttpClient は、既定で 100 秒のタイムアウトが構成されています。 GrpcChannelHttpClient を使用するように構成されている場合、実行時間の長い gRPC ストリーミング呼び出しは、タイムアウト制限内に完了しない場合は取り消されます。

System.OperationCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.

このエラーを修正するには、いくつかの方法があります。 1 つ目は、HttpClient.Timeout を大きな値に構成することです。 Timeout.InfiniteTimeSpan はタイムアウトを無効にします。

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var httpClient = new HttpClient(handler) { Timeout = Timeout.InfiniteTimeSpan };
var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpClient = httpClient });
var client = new Greeter.GreeterClient(channel);

または、HttpClient を作成せずに、代わりに GrpcChannel.HttpHandler を設定します。

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);

このドキュメントでは、.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);
    

System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport スイッチは、.NET Core 3.x. でのみ必要です。 .NET 5 では、何も実行されないため、必要ではありません。

重要

セキュリティで保護されていない gRPC サービスは、HTTP/2 専用ポートでホストする必要があります。 詳細については、ASP.NET Core プロトコルのネゴシエーションに関する記事を参照してください。

MacOS で ASP.NET Core gRPC アプリを起動できない

Kestrel では、.NET 8 より前の macOS での TLS を使用する HTTP/2 はサポートされていません。 ASP.NET Core gRPC テンプレートとサンプルでは、既定で TLS を使用しています。 gRPC サーバーを起動しようとすると、次のエラー メッセージが表示されます。

Unable to bind to https://localhost:5001 on the IPv4 loopback interface:'HTTP/2 over TLS is not supported on macOS due to missing ALPN support.'. (IPv4 ループバック インターフェイスで https://localhost:5001 にバインドできません。'HTTP/2 over TLS は ALPN サポートがないため、macOS でサポートされていません。')

.NET 7 以前でこの問題を回避するには、TLS を "使わずに" HTTP/2 を使用するように Kestrel と gRPC クライアントを構成します。 これは開発時にのみ実行してください。 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>();
        });

HTTP/2 エンドポイントが TLS を使用せずに構成されている場合、エンドポイントの ListenOptions.ProtocolHttpProtocols.Http2 に設定する必要があります。 HTTP/2 のネゴシエートに TLS が必要であるため、HttpProtocols.Http1AndHttp2 は使用できません。 TLS を使用しない場合、エンドポイントへのすべての接続が既定で HTTP/1.1 に設定され、gRPC の呼び出しが失敗します。

GRPC クライアントでも、TLS を使用しないように構成する必要があります。 詳細については、「.NET Core クライアントで安全でない gRPC サービスを呼び出す」を参照してください。

警告

TLS を使用しない HTTP/2 は、アプリの開発時にのみ使用してください。 実稼働アプリでは、常にトランスポート セキュリティを使用する必要があります。 詳細については、 gRPC for ASP.NET Core のセキュリティの考慮事項 に関するページを参照してください。

gRPC C# アセットが .proto ファイルから生成されたコードでない

具象クライアントとサービス基本クラスの gRPC コード生成には、protobuf ファイルとツールをプロジェクトから参照する必要があります。 次のものを含める必要があります。

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:型または名前空間の名前 'tMyGrpcServices' が見つかりませんでした (using ディレクティブまたはアセンブリ参照が指定されていることを確認してください)

この問題は次の方法で回避できます。

  1. 新しい .NET Core クラス ライブラリ プロジェクトを作成します。
  2. 新しいプロジェクトで、参照を追加して、.proto ファイルからの C# コード生成を有効にします。
  3. WPF アプリケーションで、新しいプロジェクトに参照を追加します。

WPF アプリケーションでは、新しいクラス ライブラリ プロジェクトから、gRPC によって生成された型を使用できます。

サブディレクトリでホストされている gRPC サービスの呼び出し

警告

多くのサードパーティ製 gRPC ツールでは、サブディレクトリでホストされているサービスはサポートされていません。 ルート ディレクトリとして gRPC をホストする方法を見つけることを検討してください。

gRPC 呼び出しを行うときに、gRPC チャネルのアドレスのパス コンポーネントは無視されます。 たとえば、GrpcChannel.ForAddress("https://localhost:5001/ignored_path") では、サービスの gRPC 呼び出しをルーティングするときに 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 に送信されます。

クライアント ファクトリは、SubdirectoryHandlerAddHttpMessageHandler を使用して構成することもできます。

HttpClient.Timeout からの gRPC 呼び出しタイムアウト

HttpClient は、既定で 100 秒のタイムアウトが構成されています。 GrpcChannelHttpClient を使用するように構成されている場合、実行時間の長い gRPC ストリーミング呼び出しは、タイムアウト制限内に完了しない場合は取り消されます。

System.OperationCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.

このエラーを修正するには、いくつかの方法があります。 1 つ目は、HttpClient.Timeout を大きな値に構成することです。 Timeout.InfiniteTimeSpan はタイムアウトを無効にします。

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var httpClient = new HttpClient(handler) { Timeout = Timeout.InfiniteTimeSpan };
var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpClient = httpClient });
var client = new Greeter.GreeterClient(channel);

または、HttpClient を作成せずに、代わりに GrpcChannel.HttpHandler を設定します。

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);

このドキュメントでは、.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);
    

System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport スイッチは、.NET Core 3.x. でのみ必要です。 .NET 5 では、何も実行されないため、必要ではありません。

重要

セキュリティで保護されていない gRPC サービスは、HTTP/2 専用ポートでホストする必要があります。 詳細については、ASP.NET Core プロトコルのネゴシエーションに関する記事を参照してください。

MacOS で ASP.NET Core gRPC アプリを起動できない

Kestrel では、.NET 8 より前の macOS での TLS を使用する HTTP/2 はサポートされていません。 ASP.NET Core gRPC テンプレートとサンプルでは、既定で TLS を使用しています。 gRPC サーバーを起動しようとすると、次のエラー メッセージが表示されます。

Unable to bind to https://localhost:5001 on the IPv4 loopback interface:'HTTP/2 over TLS is not supported on macOS due to missing ALPN support.'. (IPv4 ループバック インターフェイスで https://localhost:5001 にバインドできません。'HTTP/2 over TLS は ALPN サポートがないため、macOS でサポートされていません。')

.NET 7 以前でこの問題を回避するには、TLS を "使わずに" HTTP/2 を使用するように Kestrel と gRPC クライアントを構成します。 これは開発時にのみ実行してください。 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>();
        });

HTTP/2 エンドポイントが TLS を使用せずに構成されている場合、エンドポイントの ListenOptions.ProtocolHttpProtocols.Http2 に設定する必要があります。 HTTP/2 のネゴシエートに TLS が必要であるため、HttpProtocols.Http1AndHttp2 は使用できません。 TLS を使用しない場合、エンドポイントへのすべての接続が既定で HTTP/1.1 に設定され、gRPC の呼び出しが失敗します。

GRPC クライアントでも、TLS を使用しないように構成する必要があります。 詳細については、「.NET Core クライアントで安全でない gRPC サービスを呼び出す」を参照してください。

警告

TLS を使用しない HTTP/2 は、アプリの開発時にのみ使用してください。 実稼働アプリでは、常にトランスポート セキュリティを使用する必要があります。 詳細については、 gRPC for ASP.NET Core のセキュリティの考慮事項 に関するページを参照してください。

gRPC C# アセットが .proto ファイルから生成されたコードでない

具象クライアントとサービス基本クラスの gRPC コード生成には、protobuf ファイルとツールをプロジェクトから参照する必要があります。 次のものを含める必要があります。

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:型または名前空間の名前 'tMyGrpcServices' が見つかりませんでした (using ディレクティブまたはアセンブリ参照が指定されていることを確認してください)

この問題は次の方法で回避できます。

  1. 新しい .NET Core クラス ライブラリ プロジェクトを作成します。
  2. 新しいプロジェクトで、参照を追加して、.proto ファイルからの C# コード生成を有効にします。
  3. WPF アプリケーションで、新しいプロジェクトに参照を追加します。

WPF アプリケーションでは、新しいクラス ライブラリ プロジェクトから、gRPC によって生成された型を使用できます。

サブディレクトリでホストされている gRPC サービスの呼び出し

警告

多くのサードパーティ製 gRPC ツールでは、サブディレクトリでホストされているサービスはサポートされていません。 ルート ディレクトリとして gRPC をホストする方法を見つけることを検討してください。

gRPC 呼び出しを行うときに、gRPC チャネルのアドレスのパス コンポーネントは無視されます。 たとえば、GrpcChannel.ForAddress("https://localhost:5001/ignored_path") では、サービスの gRPC 呼び出しをルーティングするときに 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 に送信されます。

クライアント ファクトリは、SubdirectoryHandlerAddHttpMessageHandler を使用して構成することもできます。

HttpClient.Timeout からの gRPC 呼び出しタイムアウト

HttpClient は、既定で 100 秒のタイムアウトが構成されています。 GrpcChannelHttpClient を使用するように構成されている場合、実行時間の長い gRPC ストリーミング呼び出しは、タイムアウト制限内に完了しない場合は取り消されます。

System.OperationCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.

このエラーを修正するには、いくつかの方法があります。 1 つ目は、HttpClient.Timeout を大きな値に構成することです。 Timeout.InfiniteTimeSpan はタイムアウトを無効にします。

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var httpClient = new HttpClient(handler) { Timeout = Timeout.InfiniteTimeSpan };
var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpClient = httpClient });
var client = new Greeter.GreeterClient(channel);

または、HttpClient を作成せずに、代わりに GrpcChannel.HttpHandler を設定します。

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);