.NET 上的 gRPC 攔截器
注意
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 如需目前的版本,請參閱 本文的 .NET 9 版本。
攔截器是一種 gRPC 概念,可讓應用程式與傳入或傳出的 gRPC 呼叫互動。 其可提供擴充要求處理管線的方式。
攔截器會針對通道或服務進行設定,並針對每個 gRPC 呼叫自動執行。 由於攔截器對於使用者的應用程式邏輯而言是透明的,因此它們是常見案例 (例如,記錄、監視、驗證及驗證) 的絕佳解決方案。
Interceptor
類型
藉由建立繼承自 Interceptor
類型的類別,可為 gRPC 伺服器和用戶端實作攔截器:
public class ExampleInterceptor : Interceptor
{
}
根據預設,Interceptor
基底類別不會執行任何動作。 透過覆寫攔截器實作中適當的基底類別方法,將行為新增至攔截器。
用戶端攔截器
gRPC 用戶端攔截器會攔截傳出的 RPC 叫用。 其提供對已傳送要求、傳入回應及用戶端呼叫內容的存取權。
要針對用戶端覆寫的 Interceptor
方法:
BlockingUnaryCall
:攔截一元 RPC 的封鎖叫用。AsyncUnaryCall
:攔截一元 RPC 的非同步叫用。AsyncClientStreamingCall
:攔截用戶端串流 RPC 的非同步叫用。AsyncServerStreamingCall
:攔截伺服器串流 RPC 的非同步叫用。AsyncDuplexStreamingCall
:攔截雙向串流 RPC 的非同步叫用。
警告
雖然 BlockingUnaryCall
和 AsyncUnaryCall
都是指一元 RPC,但二者無法互換。 AsyncUnaryCall
不會攔截封鎖叫用,而 BlockingUnaryCall
不會攔截非同步叫用。
建立用戶端 gRPC 攔截器
下列程式碼提供攔截一元呼叫之非同步叫用的基本範例:
public class ClientLoggingInterceptor : Interceptor
{
private readonly ILogger _logger;
public ClientLoggingInterceptor(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<ClientLoggingInterceptor>();
}
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
TRequest request,
ClientInterceptorContext<TRequest, TResponse> context,
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
_logger.LogInformation("Starting call. Type/Method: {Type} / {Method}",
context.Method.Type, context.Method.Name);
return continuation(request, context);
}
}
覆寫 AsyncUnaryCall
:
- 攔截非同步一元呼叫。
- 記錄呼叫的詳細資料。
- 呼叫傳入方法的
continuation
參數。 如果這是最後一個攔截器,則會叫用鏈結中的下一個攔截器或基礎呼叫啟動程式。
對於每種服務方法,Interceptor
上的方法都有不同的簽章。 不過,continuation
和 context
參數背後的概念保持不變:
continuation
是委派,可叫用鏈結中的下一個攔截器或基礎呼叫啟動程式 (如果鏈結中沒有任何攔截器)。 呼叫零次或多次並不是錯誤。 攔截器不需要傳回從continuation
委派傳回的呼叫表示法 (如果是一元 RPC,則為AsyncUnaryCall
)。 如果省略委派呼叫並傳回您自己的呼叫表示法執行個體,則會中斷攔截器的鏈結,並立即傳回相關聯的回應。context
包含與用戶端呼叫相關聯的範圍值。 請使用context
來傳遞中繼資料,例如安全性主體、認證或追蹤資料。 此外,context
還包含期限和取消的相關資訊。 如需詳細資訊,請參閱具有期限和取消功能的可靠 gRPC 服務。
在用戶端攔截器中等候回應
攔截器可以藉由更新 AsyncUnaryCall<TResponse>.ResponseAsync
或 AsyncClientStreamingCall<TRequest, TResponse>.ResponseAsync
值,等候一元和用戶端串流呼叫中的回應。
public class ErrorHandlerInterceptor : Interceptor
{
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
TRequest request,
ClientInterceptorContext<TRequest, TResponse> context,
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
var call = continuation(request, context);
return new AsyncUnaryCall<TResponse>(
HandleResponse(call.ResponseAsync),
call.ResponseHeadersAsync,
call.GetStatus,
call.GetTrailers,
call.Dispose);
}
private async Task<TResponse> HandleResponse<TResponse>(Task<TResponse> inner)
{
try
{
return await inner;
}
catch (Exception ex)
{
throw new InvalidOperationException("Custom error", ex);
}
}
}
上述 程式碼:
- 建立覆寫
AsyncUnaryCall
的新攔截器。 - 覆寫
AsyncUnaryCall
:- 呼叫
continuation
參數以叫用攔截器鏈結中的下一個項目。 - 根據接續的結果建立新的
AsyncUnaryCall<TResponse>
執行個體。 - 使用
HandleResponse
方法包裝ResponseAsync
工作。 - 使用
HandleResponse
等候回應。 等候回應允許在用戶端收到回應之後新增邏輯。 透過在 try-catch 區塊中等候回應,即可記錄來自呼叫的錯誤。
- 呼叫
如需如何建立用戶端攔截器的詳細資訊,請參閱 grpc/grpc-dotnet
GitHub 存放庫中的 ClientLoggerInterceptor.cs
範例。
設定用戶端攔截器
gRPC 用戶端攔截器是在通道上設定。
下列程式碼範例:
- 使用
GrpcChannel.ForAddress
建立通道。 - 使用
Intercept
擴充方法將通道設定為使用攔截器。 請注意,這個方法會傳回CallInvoker
。 強型別 gRPC 用戶端可以從啟動程式 (就像通道一樣) 建立。 - 從啟動程式建立用戶端。 用戶端所發出的 gRPC 呼叫會自動執行攔截器。
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var invoker = channel.Intercept(new ClientLoggerInterceptor());
var client = new Greeter.GreeterClient(invoker);
可以鏈結 Intercept
擴充方法,為通道設定多個攔截器。 或者,使用一個可接受多個攔截器的 Intercept
多載。 您可以針對單一 gRPC 呼叫執行任意數目的攔截器,如下列範例所示:
var invoker = channel
.Intercept(new ClientTokenInterceptor())
.Intercept(new ClientMonitoringInterceptor())
.Intercept(new ClientLoggerInterceptor());
攔截器會依鏈結 Intercept
擴充方法的反向順序叫用。 在上述程式碼中,攔截器會依下列順序叫用:
ClientLoggerInterceptor
ClientMonitoringInterceptor
ClientTokenInterceptor
如需如何使用 gRPC 用戶端處理站設定攔截器的資訊,請參閱 .NET 中的 gRPC 用戶端處理站整合。
伺服器攔截器
gRPC 伺服器攔截器可攔截傳入的 RPC 要求。 其提供對傳入要求、傳出回應及伺服器端呼叫內容的存取權。
要針對伺服器覆寫的 Interceptor
方法:
UnaryServerHandler
:攔截一元 RPC。ClientStreamingServerHandler
:攔截用戶端串流 RPC。ServerStreamingServerHandler
:攔截伺服器串流 RPC。DuplexStreamingServerHandler
:攔截雙向串流 RPC。
建立伺服器 gRPC 攔截器
下列程式碼提供攔截傳入一元 RPC 的範例:
public class ServerLoggerInterceptor : Interceptor
{
private readonly ILogger _logger;
public ServerLoggerInterceptor(ILogger<ServerLoggerInterceptor> logger)
{
_logger = logger;
}
public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
TRequest request,
ServerCallContext context,
UnaryServerMethod<TRequest, TResponse> continuation)
{
_logger.LogInformation("Starting receiving call. Type/Method: {Type} / {Method}",
MethodType.Unary, context.Method);
try
{
return await continuation(request, context);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error thrown by {context.Method}.");
throw;
}
}
}
覆寫 UnaryServerHandler
:
- 攔截傳入的一元呼叫。
- 記錄呼叫的詳細資料。
- 呼叫傳入方法的
continuation
參數。 如果這是最後一個攔截器,則會叫用鏈結中的下一個攔截器或服務處理常式。 - 記錄任何例外狀況。 等候接續允許在執行服務方法之後新增邏輯。 透過在 try-catch 區塊中等候接續,即可記錄來自方法的錯誤。
用戶端與伺服器攔截器方法的簽章類似:
continuation
代表傳入 RPC 的委派,其呼叫鏈結中的下一個攔截器或服務處理常式 (如果鏈結中沒有任何攔截器)。 與用戶端攔截器類似,您可以隨時進行呼叫,而無需直接從接續委派傳回回應。 透過等候接續,即可在執行服務處理常式之後新增輸出邏輯。context
包含與伺服器端呼叫相關聯的中繼資料,例如要求中繼資料、期限和取消或 RPC 結果。
如需如何建立伺服器攔截器的詳細資訊,請參閱 grpc/grpc-dotnet
GitHub 存放庫中的 ServerLoggerInterceptor.cs
範例。
設定伺服器攔截器
gRPC 伺服器攔截器是在啟動時設定。 下列程式碼範例:
- 使用
AddGrpc
將 gRPC 新增至應用程式。 - 透過將
ServerLoggerInterceptor
新增至服務選項的Interceptors
集合,為所有服務設定該項目。
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc(options =>
{
options.Interceptors.Add<ServerLoggerInterceptor>();
});
}
您也可以使用 AddServiceOptions
並指定服務類型,為特定服務設定攔截器。
public void ConfigureServices(IServiceCollection services)
{
services
.AddGrpc()
.AddServiceOptions<GreeterService>(options =>
{
options.Interceptors.Add<ServerLoggerInterceptor>();
});
}
攔截器會依其新增至 InterceptorCollection
的順序執行。 如果已設定全域和單一服務攔截器,則會先執行全域設定的攔截器,再執行針對單一服務設定的攔截器。
根據預設,gRPC 伺服器攔截器具有每個要求的存留期。 透過使用相依性插入註冊攔截器類型,就可以覆寫此行為。 下列範例會註冊 ServerLoggerInterceptor
的單一資料庫存留期:
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc(options =>
{
options.Interceptors.Add<ServerLoggerInterceptor>();
});
services.AddSingleton<ServerLoggerInterceptor>();
}
gRPC 攔截器與中介軟體
相較於 C-core 型 gRPC 應用程式中的攔截器,ASP.NET Core 中介軟體提供類似的功能。 ASP.NET Core 中介軟體和攔截器在概念上相似。 兩個都:
- 用來建構處理 gRPC 要求的管線。
- 允許在管線的下一個元件之前或之後執行工作。
- 提供
HttpContext
的存取權:- 在中介軟體中,
HttpContext
是參數。 - 在攔截器中,可以使用
ServerCallContext
參數搭配ServerCallContext.GetHttpContext
擴充方法來存取HttpContext
。 這項功能專屬於在 ASP.NET Core 中執行的攔截器。
- 在中介軟體中,
gRPC 攔截器與 ASP.NET Core 中介軟體的差異:
- 攔截器:
- 使用
ServerCallContext
在 gRPC 抽象層上操作。 - 提供下列項目的存取權:
- 傳送至呼叫的還原序列化訊息。
- 序列化之前從呼叫傳回的訊息。
- 可以擷取並處理從 gRPC 服務擲回的例外狀況。
- 使用
- 中介軟體:
- 針對所有 HTTP 要求執行。
- 在 gRPC 攔截器之前執行。
- 對基礎 HTTP/2 訊息進行操作。
- 只能存取要求和回應串流中的位元組。