ASP.NET Core での gRPC JSON コード変換

作成者: James Newton-King

gRPC 高性能なリモート プロシージャ コール (RPC) フレームワークです。 gRPC は、HTTP/2、ストリーミング、Protobuf、メッセージ コントラクトを使用して、高パフォーマンスのリアルタイム サービスを作成します。

gRPC に関する制限事項の 1 つは、すべてのプラットフォームでそれを使用できるわけではないということです。 ブラウザーでは HTTP/2 が完全にサポートされていないため、REST API と JSON が、ブラウザー アプリにデータを取り込む主な方法になります。 gRPC には利点がありますが、REST API と JSON にも最新のアプリで重要な役割があります。 gRPC "および" JSON Web API を構築すると、アプリ開発に不要なオーバーヘッドが発生します。

このドキュメントでは、gRPC サービスを使用して JSON Web API を作成する方法について説明します。

概要

gRPC JSON コード変換は、gRPC サービス用の RESTful JSON API を作成する ASP.NET Core 向けの拡張機能です。 構成が完了すると、コード変換によって、アプリで次のような使い慣れた HTTP の概念を使って gRPC サービスを呼び出せるようになります。

  • HTTP 動詞
  • URL パラメーター バインディング
  • JSON の要求と応答

サービスの呼び出しには、gRPC も引き続き使用できます。

注意

gRPC JSON コード変換により、代替の試験段階の拡張機能である gRPC HTTP API が置き換えられます。

使用法

  1. Microsoft.AspNetCore.Grpc.JsonTranscoding へのパッケージ参照を追加します。

  2. サーバー スタートアップ コードに AddJsonTranscoding を追加してコード変換を登録する: Program.cs ファイルの builder.Services.AddGrpc();builder.Services.AddGrpc().AddJsonTranscoding(); に変更します。

  3. プロジェクト ファイル (.csproj) のプロパティ グループに <IncludeHttpRuleProtos>true</IncludeHttpRuleProtos> を追加します。

    <Project Sdk="Microsoft.NET.Sdk.Web">
    
      <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
        <InvariantGlobalization>true</InvariantGlobalization>
        <IncludeHttpRuleProtos>true</IncludeHttpRuleProtos>
      </PropertyGroup>
    
  4. HTTP のバインディングとルートを使用して、 .proto ファイル内の gRPC メソッドに注釈を付けます。

    syntax = "proto3";
    
    option csharp_namespace = "GrpcServiceTranscoding";
    import "google/api/annotations.proto";
    
    package greet;
    
    // The greeting service definition.
    service Greeter {
      rpc SayHello (HelloRequest) returns (HelloReply) {
        option (google.api.http) = {
          get: "/v1/greeter/{name}"
        };
      }
    }
    
    // The request message containing the user's name.
    message HelloRequest {
      string name = 1;
    }
    
    // The response message containing the greetings.
    message HelloReply {
      string message = 1;
    }
    

これで、SayHello gRPC メソッドを gRPC として、また JSON Web API として呼び出すことができるようになりました。

  • 要求: GET /v1/greeter/world
  • 応答: { "message": "Hello world" }

サーバーが要求ごとにログを書き込むよう構成されている場合、サーバー ログでは、gRPC サービスによって HTTP 呼び出しが実行されることが示されます。 コード変換は、受信した HTTP 要求を gRPC メッセージにマップし、応答メッセージを JSON に変換します。

info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET https://localhost:5001/v1/greeter/world
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
      Executing endpoint 'gRPC - /v1/greeter/{name}'
info: Server.GreeterService[0]
      Sending hello to world
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint 'gRPC - /v1/greeter/{name}'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 1.996ms 200 application/json

gRPC メソッドに注釈を付ける

gRPC メソッドでコード変換をサポートするには、事前に gRPC メソッドに HTTP ルールで注釈を付ける必要があります。 HTTP ルールには、HTTP メソッドやルートなど、gRPC メソッドを呼び出す方法に関する情報が含まれます。

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/v1/greeter/{name}"
    };
  }
}

上の例では:

  • SayHello メソッドを使って Greeter サービスが定義されています。 メソッドには、名前 google.api.http を使って指定された HTTP ルールがあります。
  • このメソッドには、GET 要求と /v1/greeter/{name} ルートを使ってアクセスできます。
  • 要求メッセージの name フィールドが、ルート パラメーターにバインドされています。

多くのオプションを使って、gRPC メソッドを RESTful API にバインドする方法をカスタマイズできます。 gRPC メソッドに注釈を付け、JSON をカスタマイズする方法について詳しくは、「gRPC JSON トランスコード用に HTTP と JSON を構成する」をご覧ください。

ストリーミング メソッド

従来の HTTP/2 による gRPC では、すべての方向でストリーミングがサポートされます。 コード変換は、サーバー ストリーミングのみに限定されます。 クライアント ストリーミングと双方向ストリーミングのメソッドはサポートされていません。

サーバー ストリーミング メソッドでは、行区切り JSON が使用されます。 WriteAsync を使用して書き込まれる各メッセージは、JSON にシリアル化され、その後に改行が続きます。

次のサーバー ストリーミング メソッドでは、3 つのメッセージを書き込みます。

public override async Task StreamingFromServer(ExampleRequest request,
    IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
    for (var i = 1; i <= 3; i++)
    {
        await responseStream.WriteAsync(new ExampleResponse { Text = $"Message {i}" });
        await Task.Delay(TimeSpan.FromSeconds(1));
    }
}

クライアントは、次の 3 つの行区切り JSON オブジェクトを受け取ります。

{"Text":"Message 1"}
{"Text":"Message 2"}
{"Text":"Message 3"}

WriteIndentedJSON 設定はサーバー ストリーミング メソッドには適用されないことに注意してください。 再フォーマットを行うと、改行と空白文字が JSON に追加されます。これは、行区切り JSON では使用できません。

ASP.NET Core gPRC コード変換とストリーミング アプリのサンプルを確認するかダウンロードします。

HTTP プロトコル

.NET SDK に含まれる ASP.NET Core gRPC サービス テンプレートを使うと、HTTP/2 専用に構成されたアプリを作成できます。 HTTP/2 は、アプリが従来の HTTP/2 経由の gRPC のみをサポートする場合に適した既定値です。 一方、コード変換は 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 が必要です。 gRPC アプリでの HTTP プロトコルの構成について詳しくは、ASP.NET Core gRPC のプロトコル ネゴシエーションに関する記事を参照してください。

gRPC JSON コード変換と gRPC-Web

コード変換でも gRPC-Web でも、gRPC サービスをブラウザーから呼び出すことができます。 ただし、それぞれの動作は次のように異なります。

  • gRPC-Web では、ブラウザー アプリが gRPC-Web クライアントと Protobuf を使用してブラウザーから gRPC サービスを呼び出すことができます。 gRPC-Web では、ブラウザー アプリで gRPC クライアントを生成する必要があり、小さい Protobuf メッセージをすばやく送信できるという利点があります。
  • コード変換を使用すると、JSON と RESTful API のように、ブラウザー アプリで gRPC サービスを呼び出すことができます。 ブラウザー アプリで gRPC クライアントを生成する必要はありません。gRPC について知る必要もありません。

前の Greeter サービスは、ブラウザーの JavaScript API を使用して呼び出すことができます。

var name = nameInput.value;

fetch('/v1/greeter/' + name)
  .then((response) => response.json())
  .then((result) => {
    console.log(result.message);
    // Hello world
  });

grpc-gateway

grpc-gateway は、gRPC サービスから RESTful JSON API を作成するための別のテクノロジです。 同じ .proto の注釈を使用して、HTTP の概念を gRPC サービスにマップします。

grpc-gateway では、コード生成を使用してリバースプロキシ サーバーが作成されます。 リバース プロキシによって RESTful 呼び出しが gRPC+Protobuf に変換され、その呼び出しが HTTP/2 経由で gRPC サービスに送信されます。 この方法の利点は、gRPC サービスが RESTful JSON API について認識しない点です。 どの gRPC サーバーでも grpc-gateway を使用できます。

一方、gRPC JSON コード変換は、ASP.NET Core アプリ内で実行されます。 JSON を Protobuf メッセージに逆シリアル化した後、gRPC サービスを直接呼び出します。 ASP.NET Core のコード変換は、.NET アプリ開発者に次のような利点をもたらします。

  • 複雑さが減る: gRPC サービスとマップされた RESTful JSON API の両方が、1 つの ASP.NET Core アプリから実行されます。
  • パフォーマンスの向上: コード変換により、JSON が Protobuf メッセージに逆シリアル化され、gRPC サービスが直接呼び出されます。 別のサーバーへの新しい gRPC 呼び出しを行うことに対して、インプロセスでこれを実行するとパフォーマンス上の大きな利点があります。
  • 低コスト: サーバーの数が少ないほど、毎月のホスティング料金が安くなります。

grpc-gateway のインストールと使用については、grpc-gateway の README を参照してください。

その他のリソース

gRPC 高性能なリモート プロシージャ コール (RPC) フレームワークです。 gRPC は、HTTP/2、ストリーミング、Protobuf、メッセージ コントラクトを使用して、高パフォーマンスのリアルタイム サービスを作成します。

gRPC に関する制限事項の 1 つは、すべてのプラットフォームでそれを使用できるわけではないということです。 ブラウザーでは HTTP/2 が完全にサポートされていないため、REST API と JSON が、ブラウザー アプリにデータを取り込む主な方法になります。 gRPC には利点がありますが、REST API と JSON にも最新のアプリで重要な役割があります。 gRPC "および" JSON Web API を構築すると、アプリ開発に不要なオーバーヘッドが発生します。

このドキュメントでは、gRPC サービスを使用して JSON Web API を作成する方法について説明します。

概要

gRPC JSON コード変換は、gRPC サービス用の RESTful JSON API を作成する ASP.NET Core 向けの拡張機能です。 構成が完了すると、コード変換によって、アプリで次のような使い慣れた HTTP の概念を使って gRPC サービスを呼び出せるようになります。

  • HTTP 動詞
  • URL パラメーター バインディング
  • JSON の要求と応答

サービスの呼び出しには、gRPC も引き続き使用できます。

注意

gRPC JSON コード変換により、代替の試験段階の拡張機能である gRPC HTTP API が置き換えられます。

使用法

  1. Microsoft.AspNetCore.Grpc.JsonTranscoding へのパッケージ参照を追加します。
  2. サーバー スタートアップ コードに AddJsonTranscoding を追加してコード変換を登録する: Program.cs ファイルの builder.Services.AddGrpc();builder.Services.AddGrpc().AddJsonTranscoding(); に変更します。
  3. .csproj ファイルが含まれるプロジェクト ディレクトリに、ディレクトリ構造 /google/api を作成します。
  4. /google/api ディレクトリに、google/api/http.protogoogle/api/annotations.proto ファイルを追加します。
  5. HTTP のバインディングとルートを使用して、 .proto ファイル内の gRPC メソッドに注釈を付けます。
syntax = "proto3";

option csharp_namespace = "GrpcServiceTranscoding";
import "google/api/annotations.proto";

package greet;

// The greeting service definition.
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/v1/greeter/{name}"
    };
  }
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings.
message HelloReply {
  string message = 1;
}

これで、SayHello gRPC メソッドを gRPC として、また JSON Web API として呼び出すことができるようになりました。

  • 要求: GET /v1/greeter/world
  • 応答: { "message": "Hello world" }

サーバーが要求ごとにログを書き込むよう構成されている場合、サーバー ログでは、gRPC サービスによって HTTP 呼び出しが実行されることが示されます。 コード変換は、受信した HTTP 要求を gRPC メッセージにマップし、応答メッセージを JSON に変換します。

info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET https://localhost:5001/v1/greeter/world
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
      Executing endpoint 'gRPC - /v1/greeter/{name}'
info: Server.GreeterService[0]
      Sending hello to world
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint 'gRPC - /v1/greeter/{name}'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 1.996ms 200 application/json

gRPC メソッドに注釈を付ける

gRPC メソッドでコード変換をサポートするには、事前に gRPC メソッドに HTTP ルールで注釈を付ける必要があります。 HTTP ルールには、HTTP メソッドやルートなど、gRPC メソッドを呼び出す方法に関する情報が含まれます。

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/v1/greeter/{name}"
    };
  }
}

上の例では:

  • SayHello メソッドを使って Greeter サービスが定義されています。 メソッドには、名前 google.api.http を使って指定された HTTP ルールがあります。
  • このメソッドには、GET 要求と /v1/greeter/{name} ルートを使ってアクセスできます。
  • 要求メッセージの name フィールドが、ルート パラメーターにバインドされています。

多くのオプションを使って、gRPC メソッドを RESTful API にバインドする方法をカスタマイズできます。 gRPC メソッドに注釈を付け、JSON をカスタマイズする方法について詳しくは、「gRPC JSON トランスコード用に HTTP と JSON を構成する」をご覧ください。

ストリーミング メソッド

従来の HTTP/2 による gRPC では、すべての方向でストリーミングがサポートされます。 コード変換は、サーバー ストリーミングのみに限定されます。 クライアント ストリーミングと双方向ストリーミングのメソッドはサポートされていません。

サーバー ストリーミング メソッドでは、行区切り JSON が使用されます。 WriteAsync を使用して書き込まれる各メッセージは、JSON にシリアル化され、その後に改行が続きます。

次のサーバー ストリーミング メソッドでは、3 つのメッセージを書き込みます。

public override async Task StreamingFromServer(ExampleRequest request,
    IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
    for (var i = 1; i <= 3; i++)
    {
        await responseStream.WriteAsync(new ExampleResponse { Text = $"Message {i}" });
        await Task.Delay(TimeSpan.FromSeconds(1));
    }
}

クライアントは、次の 3 つの行区切り JSON オブジェクトを受け取ります。

{"Text":"Message 1"}
{"Text":"Message 2"}
{"Text":"Message 3"}

WriteIndentedJSON 設定はサーバー ストリーミング メソッドには適用されないことに注意してください。 再フォーマットを行うと、改行と空白文字が JSON に追加されます。これは、行区切り JSON では使用できません。

ASP.NET Core gPRC コード変換とストリーミング アプリのサンプルを確認するかダウンロードします。

HTTP プロトコル

.NET SDK に含まれる ASP.NET Core gRPC サービス テンプレートを使うと、HTTP/2 専用に構成されたアプリを作成できます。 HTTP/2 は、アプリが従来の HTTP/2 経由の gRPC のみをサポートする場合に適した既定値です。 一方、コード変換は 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 が必要です。 gRPC アプリでの HTTP プロトコルの構成について詳しくは、ASP.NET Core gRPC のプロトコル ネゴシエーションに関する記事を参照してください。

gRPC JSON コード変換と gRPC-Web

コード変換でも gRPC-Web でも、gRPC サービスをブラウザーから呼び出すことができます。 ただし、それぞれの動作は次のように異なります。

  • gRPC-Web では、ブラウザー アプリが gRPC-Web クライアントと Protobuf を使用してブラウザーから gRPC サービスを呼び出すことができます。 gRPC-Web では、ブラウザー アプリで gRPC クライアントを生成する必要があり、小さい Protobuf メッセージをすばやく送信できるという利点があります。
  • コード変換を使用すると、JSON と RESTful API のように、ブラウザー アプリで gRPC サービスを呼び出すことができます。 ブラウザー アプリで gRPC クライアントを生成する必要はありません。gRPC について知る必要もありません。

前の Greeter サービスは、ブラウザーの JavaScript API を使用して呼び出すことができます。

var name = nameInput.value;

fetch('/v1/greeter/' + name)
  .then((response) => response.json())
  .then((result) => {
    console.log(result.message);
    // Hello world
  });

grpc-gateway

grpc-gateway は、gRPC サービスから RESTful JSON API を作成するための別のテクノロジです。 同じ .proto の注釈を使用して、HTTP の概念を gRPC サービスにマップします。

grpc-gateway では、コード生成を使用してリバースプロキシ サーバーが作成されます。 リバース プロキシによって RESTful 呼び出しが gRPC+Protobuf に変換され、その呼び出しが HTTP/2 経由で gRPC サービスに送信されます。 この方法の利点は、gRPC サービスが RESTful JSON API について認識しない点です。 どの gRPC サーバーでも grpc-gateway を使用できます。

一方、gRPC JSON コード変換は、ASP.NET Core アプリ内で実行されます。 JSON を Protobuf メッセージに逆シリアル化した後、gRPC サービスを直接呼び出します。 ASP.NET Core のコード変換は、.NET アプリ開発者に次のような利点をもたらします。

  • 複雑さが減る: gRPC サービスとマップされた RESTful JSON API の両方が、1 つの ASP.NET Core アプリから実行されます。
  • パフォーマンスの向上: コード変換により、JSON が Protobuf メッセージに逆シリアル化され、gRPC サービスが直接呼び出されます。 別のサーバーへの新しい gRPC 呼び出しを行うことに対して、インプロセスでこれを実行するとパフォーマンス上の大きな利点があります。
  • 低コスト: サーバーの数が少ないほど、毎月のホスティング料金が安くなります。

grpc-gateway のインストールと使用については、grpc-gateway の README を参照してください。

その他のリソース