針對 .NET 上的 gRPC 進行疑難排解
注意
這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .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 服務還有一些額外需求:
- .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 服務:
重要
不安全的 gRPC 服務必須裝載在僅限 HTTP/2 的連接埠上。 如需詳細資訊,請參閱 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# 程式碼的功能:- 新增下列套件參考:
- 將
.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 網頁伺服器使用 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 工具在其配套原生二進位檔案不支援的環境中執行。
建置或取得 protoc
和 grpc_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.
# 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 秒的逾時。 如果 GrpcChannel
設定為使用 HttpClient
,當長時間執行的 gRPC 串流呼叫未在逾時限制內完成時,這些呼叫就會取消。
System.OperationCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.
有幾種方法可以修正此錯誤。 第一個是將 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);
只有 .NET Core 3.x 才需要 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport
參數。 該參數不會在 .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 伺服器時,將會看到下列錯誤訊息:
無法在 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
。 由於需要 TLS 才能交涉 HTTP/2,因此無法使用 HttpProtocols.Http1AndHttp2
。 如果未使用 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# 程式碼的功能:- 新增下列套件參考:
- 將
.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 的資訊,請參閱搭配 ASP.NET Core Kestrel 網頁伺服器使用 HTTP/3。
預設會啟用 .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 工具在其配套原生二進位檔案不支援的環境中執行。
建置或取得 protoc
和 grpc_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.
# 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 秒的逾時。 如果 GrpcChannel
設定為使用 HttpClient
,當長時間執行的 gRPC 串流呼叫未在逾時限制內完成時,這些呼叫就會取消。
System.OperationCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.
有幾種方法可以修正此錯誤。 第一個是將 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);
只有 .NET Core 3.x 才需要 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport
參數。 該參數不會在 .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 伺服器時,將會看到下列錯誤訊息:
無法在 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
。 由於需要 TLS 才能交涉 HTTP/2,因此無法使用 HttpProtocols.Http1AndHttp2
。 如果未使用 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# 程式碼的功能:- 新增下列套件參考:
- 將
.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
。
來自 HttpClient.Timeout
的 gRPC 呼叫逾時
HttpClient 會預設 100 秒的逾時。 如果 GrpcChannel
設定為使用 HttpClient
,當長時間執行的 gRPC 串流呼叫未在逾時限制內完成時,這些呼叫就會取消。
System.OperationCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.
有幾種方法可以修正此錯誤。 第一個是將 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);
只有 .NET Core 3.x 才需要 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport
參數。 該參數不會在 .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 伺服器時,將會看到下列錯誤訊息:
無法在 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
。 由於需要 TLS 才能交涉 HTTP/2,因此無法使用 HttpProtocols.Http1AndHttp2
。 如果未使用 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# 程式碼的功能:- 新增下列套件參考:
- 將
.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
。
來自 HttpClient.Timeout
的 gRPC 呼叫逾時
HttpClient 會預設 100 秒的逾時。 如果 GrpcChannel
設定為使用 HttpClient
,當長時間執行的 gRPC 串流呼叫未在逾時限制內完成時,這些呼叫就會取消。
System.OperationCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.
有幾種方法可以修正此錯誤。 第一個是將 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);