ASP.NET Core 中的 gRPC JSON 轉碼

作者:James Newton-King

gRPC 是高效能的遠端程序呼叫 (RPC) 架構。 gRPC 使用 HTTP/2、串流、Protobuf 和訊息合約來建立高效能、即時的服務。

gRPC 有一個限制是其並非適用於所有的平台。 瀏覽器並不完全支援 HTTP/2,這使得 REST API 和 JSON 成為將資料放入瀏覽器應用程式的主要方式。 儘管 gRPC 帶來的好處,REST API 和 JSON 在現代化應用程式中佔有重要地位。 建置 gRPC JSON Web API 會為應用程式開發增加不必要的額外負荷。

本文件討論如何使用 gRPC 服務建立 JSON Web API。

概觀

gRPC JSON 轉碼是 ASP.NET Core 的擴充功能,可針對 gRPC 服務建立 RESTful JSON API。 設定之後,轉碼可讓應用程式使用熟悉的 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. <IncludeHttpRuleProtos>true</IncludeHttpRuleProtos> 新增至專案檔 (.csproj) 中的屬性群組:

    <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 方法必須以 HTTP 規則標註,才能支援轉碼。 HTTP 規則包含如何呼叫 gRPC 方法的相關資訊,例如 HTTP 方法和路由。

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,後面接著一新行。

下列伺服器串流方法會寫入三則訊息:

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

用戶端會收到三個以行分隔的 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 的傳統 gRPC 時,HTTP/2 是很好的預設值。 不過,轉碼適用於 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 訊息的優點。
  • 轉碼可讓瀏覽器應用程式呼叫 gRPC 服務,就像是使用 JSON 的 RESTful API 一樣。 瀏覽器應用程式不需要產生 gRPC 用戶端或知道 gRPC 的任何資訊。

您可以使用瀏覽器 JavaScript API 來呼叫先前的 Greeter 服務:

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 的技術。 grpc-gateway 使用相同的 .proto 註釋,將 HTTP 概念對應至 gRPC 服務。

grpc-gateway 會使用程式碼產生來建立反向 Proxy 伺服器。 反向 Proxy 會將 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 都是從一個 ASP.NET Core 應用程式開始運行。
  • 更好的效能:將 JSON 轉碼還原序列化為 Protobuf 訊息,並直接叫用 gRPC 服務。 與對不同伺服器進行新的 gRPC 呼叫相比,在同處理序執行此動作具有顯著的效能優勢。
  • 成本較低:伺服器越少,每月的裝載費用就越少。

如需 grpc-gateway 的安裝和使用方式,請參閱 grpc-gateway 讀我檔案

其他資源

gRPC 是高效能的遠端程序呼叫 (RPC) 架構。 gRPC 使用 HTTP/2、串流、Protobuf 和訊息合約來建立高效能、即時的服務。

gRPC 有一個限制是其並非適用於所有的平台。 瀏覽器並不完全支援 HTTP/2,這使得 REST API 和 JSON 成為將資料放入瀏覽器應用程式的主要方式。 儘管 gRPC 帶來的好處,REST API 和 JSON 在現代化應用程式中佔有重要地位。 建置 gRPC JSON Web API 會為應用程式開發增加不必要的額外負荷。

本文件討論如何使用 gRPC 服務建立 JSON Web API。

概觀

gRPC JSON 轉碼是 ASP.NET Core 的擴充功能,可針對 gRPC 服務建立 RESTful JSON API。 設定之後,轉碼可讓應用程式使用熟悉的 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/http.protogoogle/api/annotations.proto 檔案新增至 /google/api 目錄。
  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 方法必須以 HTTP 規則標註,才能支援轉碼。 HTTP 規則包含如何呼叫 gRPC 方法的相關資訊,例如 HTTP 方法和路由。

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,後面接著一新行。

下列伺服器串流方法會寫入三則訊息:

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

用戶端會收到三個以行分隔的 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 的傳統 gRPC 時,HTTP/2 是很好的預設值。 不過,轉碼適用於 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 訊息的優點。
  • 轉碼可讓瀏覽器應用程式呼叫 gRPC 服務,就像是使用 JSON 的 RESTful API 一樣。 瀏覽器應用程式不需要產生 gRPC 用戶端或知道 gRPC 的任何資訊。

您可以使用瀏覽器 JavaScript API 來呼叫先前的 Greeter 服務:

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 的技術。 grpc-gateway 使用相同的 .proto 註釋,將 HTTP 概念對應至 gRPC 服務。

grpc-gateway 會使用程式碼產生來建立反向 Proxy 伺服器。 反向 Proxy 會將 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 都是從一個 ASP.NET Core 應用程式開始運行。
  • 更好的效能:將 JSON 轉碼還原序列化為 Protobuf 訊息,並直接叫用 gRPC 服務。 與對不同伺服器進行新的 gRPC 呼叫相比,在同處理序執行此動作具有顯著的效能優勢。
  • 成本較低:伺服器越少,每月的裝載費用就越少。

如需 grpc-gateway 的安裝和使用方式,請參閱 grpc-gateway 讀我檔案

其他資源