Intercepteurs gRPC sur .NET
Remarque
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 9 de cet article.
Avertissement
Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la stratégie de support .NET et .NET Core. Pour la version actuelle, consultez la version .NET 9 de cet article.
Important
Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.
Pour la version actuelle, consultez la version .NET 9 de cet article.
Par Ernest Nguyen
Les intercepteurs sont un concept gRPC qui permet aux applications d’interagir avec les appels gRPC entrants ou sortants. Ils offrent un moyen d’enrichir le pipeline de traitement des requêtes.
Les intercepteurs sont configurés pour un canal ou un service et exécutés automatiquement pour chaque appel gRPC. Étant donné que les intercepteurs sont transparents par rapport à la logique d’application de l’utilisateur, ils constituent une excellente solution pour les cas courants, comme la journalisation, la surveillance, l’authentification et la validation.
TypeInterceptor
Les intercepteurs peuvent être implémentés pour les serveurs et les clients gRPC en créant une classe qui hérite du type Interceptor
:
public class ExampleInterceptor : Interceptor
{
}
Par défaut, la classe de base Interceptor
ne fait rien. Ajoutez un comportement à un intercepteur en remplaçant les méthodes de classe de base appropriées dans une implémentation d’intercepteur.
Intercepteurs clients
Les intercepteurs clients gRPC interceptent les appels RPC sortants. Ils fournissent l’accès à la requête envoyée, à la réponse entrante et au contexte d’un appel côté client.
Méthodes Interceptor
à remplacer pour le client :
BlockingUnaryCall
: intercepte un appel bloquant d’un RPC unaire.AsyncUnaryCall
: intercepte un appel asynchrone d’un RPC unaire.AsyncClientStreamingCall
: intercepte un appel asynchrone d’un RPC de diffusion en continu client.AsyncServerStreamingCall
: intercepte un appel asynchrone d’un RPC de diffusion en continu de serveur.AsyncDuplexStreamingCall
: intercepte un appel asynchrone d’un RPC de diffusion en continu bidirectionnel.
Avertissement
Bien que BlockingUnaryCall
et AsyncUnaryCall
fassent référence à des RPC unaires, ils ne sont pas interchangeables. Un appel bloquant n’est pas intercepté par AsyncUnaryCall
, et un appel asynchrone n’est pas intercepté par BlockingUnaryCall
.
Créer un intercepteur gRPC client
Le code suivant présente un exemple de base d’interception d’un appel asynchrone d’un appel unaire :
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);
}
}
Remplacement de AsyncUnaryCall
:
- Intercepte un appel unaire asynchrone.
- Consigne les détails de l’appel.
- Appelle le paramètre
continuation
passé dans la méthode. Cela appelle l’intercepteur suivant dans la chaîne ou l’appelant sous-jacent s’il s’agit du dernier intercepteur.
Les méthodes sur Interceptor
pour chaque type de méthode de service ont des signatures différentes. Toutefois, le concept derrière les paramètres continuation
et context
reste le même :
continuation
est un délégué qui appelle l’intercepteur suivant dans la chaîne ou l’appelant sous-jacent (s’il n’y a plus d’intercepteur dans la chaîne). Ce n’est pas une erreur de l’appeler zéro ou plusieurs fois. Les intercepteurs ne sont pas tenus de retourner une représentation d’appel (AsyncUnaryCall
en cas de RPC unaire) retournée par le déléguécontinuation
. L’omission de l’appel délégué et le retour de votre propre instance de représentation d’appel interrompt la chaîne des intercepteurs et retourne immédiatement la réponse associée.context
comporte des valeurs délimitées associées à l’appel côté client. Utilisezcontext
pour transmettre des métadonnées, comme des principaux de sécurité, des informations d’identification ou des données de suivi. En outre,context
contient des informations sur les échéances et l’annulation. Pour plus d’informations, consultez Services gRPC fiables avec des échéances et des annulations.
Attente de réponse dans l’intercepteur client
Un intercepteur peut attendre la réponse dans les appels unaires et de diffusion en continu client en mettant à jour la valeur AsyncUnaryCall<TResponse>.ResponseAsync
ou 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);
}
}
}
Le code précédent :
- Crée un intercepteur qui remplace
AsyncUnaryCall
. - Remplacement de
AsyncUnaryCall
:- Appelle le paramètre
continuation
pour appeler l’élément suivant dans la chaîne d’intercepteurs. - Crée une instance
AsyncUnaryCall<TResponse>
basée sur le résultat de la continuation. - Encapsule la tâche
ResponseAsync
à l’aide de la méthodeHandleResponse
. - Attend la réponse avec
HandleResponse
. L’attente de la réponse permet d’ajouter une logique après la réception de la réponse par le client. En attendant la réponse dans un bloc try-catch, les erreurs des appels peuvent être consignées.
- Appelle le paramètre
Pour plus d’informations sur la création d’un intercepteur client, consultez l’exemple ClientLoggerInterceptor.cs
dans le dépôt GitHub grpc/grpc-dotnet
.
Configurer des intercepteurs clients
Les intercepteurs clients gRPC sont configurés sur un canal.
Le code suivant :
- Crée un canal à l’aide de
GrpcChannel.ForAddress
. - Utilise la méthode d’extension
Intercept
pour configurer le canal pour utiliser l’intercepteur. Notez que cette méthode retourne unCallInvoker
. Les clients gRPC fortement typés peuvent être créés à partir d’un appelant comme un canal. - Crée un client à partir de l’appelant. Les appels gRPC effectués par le client exécutent automatiquement l’intercepteur.
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var invoker = channel.Intercept(new ClientLoggerInterceptor());
var client = new Greeter.GreeterClient(invoker);
La méthode d’extension Intercept
peut être chaînée pour configurer plusieurs intercepteurs pour un canal. Vous pouvez également utiliser une surcharge Intercept
qui accepte plusieurs intercepteurs. Un nombre quelconque d’intercepteurs peut être exécuté pour un seul appel gRPC, comme le montre l’exemple suivant :
var invoker = channel
.Intercept(new ClientTokenInterceptor())
.Intercept(new ClientMonitoringInterceptor())
.Intercept(new ClientLoggerInterceptor());
Les intercepteurs sont appelés dans l’ordre inverse des méthodes d’extension Intercept
chaînées. Dans le code précédent, les intercepteurs sont appelés dans l’ordre suivant :
ClientLoggerInterceptor
ClientMonitoringInterceptor
ClientTokenInterceptor
Pour plus d’informations sur la configuration des intercepteurs avec la fabrique de clients gRPC, consultez Intégration de la fabrique de clients gRPC dans .NET.
Intercepteurs de serveur
Les intercepteurs de serveur gRPC interceptent les requêtes RPC entrantes. Ils fournissent l’accès à la requête entrante, à la réponse sortante et au contexte d’un appel côté serveur.
Méthodes Interceptor
à remplacer pour le serveur :
UnaryServerHandler
: intercepte un RPC unaire.ClientStreamingServerHandler
: intercepte un RPC de diffusion en continu client.ServerStreamingServerHandler
: intercepte un RPC de diffusion en continu de serveur.DuplexStreamingServerHandler
: intercepte un RPC de diffusion en continu bidirectionnel.
Créer un intercepteur gRPC de serveur
Le code suivant présente un exemple d’interception d’un RPC unaire entrant :
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;
}
}
}
Remplacement de UnaryServerHandler
:
- Intercepte un appel unaire entrant.
- Consigne les détails de l’appel.
- Appelle le paramètre
continuation
passé dans la méthode. Cela appelle l’intercepteur suivant dans la chaîne ou le gestionnaire de service s’il s’agit du dernier intercepteur. - Journalise toutes les exceptions. L’attente de la continuation permet d’ajouter la logique après l’exécution de la méthode de service. En attendant la continuation dans un bloc try-catch, les erreurs des méthodes peuvent être consignées.
La signature des méthodes des intercepteurs clients et de serveur est similaire :
continuation
signifie délégué pour un RPC entrant appelant l’intercepteur suivant dans la chaîne ou le gestionnaire de service (s’il n’y a plus d’intercepteur dans la chaîne). À l’instar des intercepteurs clients, vous pouvez l’appeler à tout moment, et il n’est pas nécessaire de retourner une réponse directement à partir du délégué de continuation. Une logique sortante peut être ajoutée après l’exécution d’un gestionnaire de service en attendant la continuation.context
transporte les métadonnées associées à l’appel côté serveur, comme les métadonnées de requête, les échéances et l’annulation, ou le résultat RPC.
Pour plus d’informations sur la création d’un intercepteur de serveur, consultez l’exemple ServerLoggerInterceptor.cs
dans le dépôt GitHub grpc/grpc-dotnet
.
Configurer des intercepteurs de serveur
Les intercepteurs de serveur gRPC sont configurés au démarrage. Le code suivant :
- Ajoute gRPC à l’application avec
AddGrpc
. - Configure
ServerLoggerInterceptor
pour tous les services en l’ajoutant à la collection deInterceptors
de l’option de service.
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc(options =>
{
options.Interceptors.Add<ServerLoggerInterceptor>();
});
}
Un intercepteur peut également être configuré pour un service spécifique en utilisant AddServiceOptions
et en spécifiant le type de service.
public void ConfigureServices(IServiceCollection services)
{
services
.AddGrpc()
.AddServiceOptions<GreeterService>(options =>
{
options.Interceptors.Add<ServerLoggerInterceptor>();
});
}
Les intercepteurs sont exécutés dans l’ordre dans lequel ils sont ajoutés à InterceptorCollection
. Si les intercepteurs de service global et unique sont configurés, les intercepteurs globalement configurés sont exécutés avant ceux configurés pour un seul service.
Par défaut, les intercepteurs de serveur gRPC ont une durée de vie par requête. Le remplacement de ce comportement est possible en inscrivant le type d’intercepteur avec l’injection de dépendances. L’exemple suivant inscrit le ServerLoggerInterceptor
avec une durée de vie singleton :
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc(options =>
{
options.Interceptors.Add<ServerLoggerInterceptor>();
});
services.AddSingleton<ServerLoggerInterceptor>();
}
Intercepteurs gRPC et intergiciels
L’intergiciel ASP.NET Core offre des fonctionnalités similaires à celles des intercepteurs dans les applications gRPC basées sur C-core. Les intergiciels ASP.NET Core et les intercepteurs sont conceptuellement similaires. Les deux :
- Ils sont utilisés pour construire un pipeline qui gère une requête gRPC.
- Permettent au travail d’être effectué avant ou après le composant suivant dans le pipeline.
- Fournissent l’accès à
HttpContext
:- Dans l’intergiciel,
HttpContext
est un paramètre. - Dans les intercepteurs,
HttpContext
est accessible à l’aide du paramètreServerCallContext
avec la méthode d’extensionServerCallContext.GetHttpContext
. Cette fonctionnalité est spécifique aux intercepteurs qui s’exécutent dans ASP.NET Core.
- Dans l’intergiciel,
Différences entre l’intercepteur gRPC et l’intergiciel ASP.NET Core :
- Les intercepteurs :
- Utilisent la couche d’abstraction gRPC à l’aide de
ServerCallContext
. - Fournissent l’accès aux éléments suivants :
- Le message désérialisé envoyé à un appel.
- Le message retourné par l’appel avant sa sérialisation.
- Peuvent intercepter et gérer les exceptions levées à partir des services gRPC.
- Utilisent la couche d’abstraction gRPC à l’aide de
- Intergiciel (middleware) :
- S’exécute pour toutes les requêtes HTTP.
- S’exécute avant les intercepteurs gRPC.
- Fonctionne sur les messages HTTP/2 sous-jacents.
- Peut accéder uniquement aux octets des flux de requête et de réponse.