Partager via


Gestion des erreurs avec gRPC

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 James Newton-King

Cet article traite de la gestion des erreurs et de gRPC :

  • Fonctionnalités intégrées de gestion des erreurs à l’aide des codes d’état et des messages d’erreur gRPC
  • Envoi d’informations complexes et structurées sur les erreurs à l’aide d’une gestion complète des erreurs

Gestion des erreurs intégrée

Les appels gRPC communiquent la réussite ou l’échec avec un code d’état. Lorsqu’un appel gRPC se termine correctement, le serveur retourne un état OK au client. Si une erreur se produit, gRPC retourne :

  • Un code d’état d’erreur, tel que CANCELLED ou UNAVAILABLE
  • Un message d’erreur de chaîne facultatif

Les types couramment utilisés avec la gestion des erreurs sont les suivants :

  • StatusCode : énumération des codes d’état gRPC. OK indique la réussite, tandis que les autres valeurs sont des échecs.
  • Status : une struct qui combine un StatusCode et un message d’erreur de chaîne facultatif. Le message d’erreur fournit des détails supplémentaires sur ce qui s’est passé.
  • RpcException : type d’exception pourvu de la valeur Status. Cette exception est levée dans les méthodes serveur gRPC et interceptée par les clients gRPC.

La gestion des erreurs intégrée prend uniquement en charge un code d’état et une description de chaîne. Pour envoyer des informations sur des erreurs complexes du serveur au client, utilisez une gestion complète des erreurs.

Levée d’erreurs de serveur

Un appel de serveur gRPC retourne toujours un état. Le serveur retourne automatiquement OK lorsqu’une méthode se termine correctement.

public class GreeterService : GreeterBase
{
    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        return Task.FromResult(new HelloReply { Message = $"Hello {request.Name}" });
    }

    public override async Task SayHelloStreaming(HelloRequest request,
        IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
    {
        for (var i = 0; i < 5; i++)
        {
            await responseStream.WriteAsync(new HelloReply { Message = $"Hello {request.Name} {i}" });
            await Task.Delay(TimeSpan.FromSeconds(1));
        }
    }
}

Le code précédent :

  • Implémente la méthode SayHello unaire qui se termine correctement lorsqu’elle retourne un message de réponse.
  • Implémente la méthode SayHelloStreaming de diffusion en continu du serveur qui se termine correctement une fois la méthode terminée.

État d’erreur du serveur

Les méthodes gRPC retournent un code d’état d’erreur en levant une exception. Lorsqu’une RpcException est levée sur le serveur, son code d’état et sa description sont retournés au client :

public class GreeterService : GreeterBase
{
    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        if (string.IsNullOrEmpty(request.Name))
        {
            throw new RpcException(new Status(StatusCode.InvalidArgument, "Name is required."));
        }
        return Task.FromResult(new HelloReply { Message = $"Hello {request.Name}" });
    }
}

Les types d’exceptions levées qui ne sont pas RpcException provoquent également l’échec de l’appel, mais avec un code d’état UNKNOWN et un message générique Exception was thrown by handler.

Exception was thrown by handler est envoyé au client plutôt que le message d’exception pour empêcher l’exposition d’informations potentiellement sensibles. Pour afficher un message d’erreur plus descriptif dans un environnement de développement, configurez EnableDetailedErrors.

Gestion des erreurs de client

Lorsqu’un client gRPC effectue un appel, le code d’état est automatiquement validé lors de l’accès à la réponse. Par exemple, l’attente d’un appel gRPC unaire retourne le message envoyé par le serveur si l’appel aboutit et lève une RpcException en cas d’échec. Interceptez RpcException pour gérer les erreurs d’un client :

var client = new Greet.GreeterClient(channel);

try
{
    var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });
    Console.WriteLine("Greeting: " + response.Message);
}
catch (RpcException ex)
{
    Console.WriteLine("Status code: " + ex.Status.StatusCode);
    Console.WriteLine("Message: " + ex.Status.Detail);
}

Le code précédent :

  • Effectue un appel gRPC unaire vers la méthode SayHello.
  • Écrit le message de réponse dans la console en cas de réussite.
  • Intercepte RpcException et écrit les détails de l’erreur en cas d’échec.

Scénarios d’erreur

Les erreurs sont représentées par RpcException avec un code d’état d’erreur et un message de détail facultatif. RpcException est levée dans de nombreux scénarios :

  • L’appel a échoué sur le serveur, qui a envoyé un code d’état d’erreur. Par exemple, le client gRPC a démarré un appel auquel des données requises manquaient à partir du message de demande et le serveur retourne un code d’état INVALID_ARGUMENT.
  • Une erreur s’est produite à l’intérieur du client lors de l’appel gRPC. Par exemple, un client effectue un appel gRPC, ne peut pas se connecter au serveur et génère une erreur avec l’état UNAVAILABLE.
  • Le CancellationToken transmis à l’appel gRPC est annulé. L’appel gRPC est arrêté et le client lève une erreur avec l’état CANCELLED.
  • Un appel gRPC dépasse l’échéance configurée. L’appel gRPC est arrêté et le client lève une erreur avec l’état DEADLINE_EXCEEDED.

Gestion complète des erreurs

La gestion complète des erreurs permet d’envoyer des informations complexes et structurées avec des messages d’erreur. Par exemple, la validation des champs de message entrants qui retourne une liste de noms et de descriptions de champs non valides. Le modèle d’erreur google.rpc.Status est souvent utilisé pour envoyer des informations sur des erreurs complexes entre les applications gRPC.

gRPC sur .NET prend en charge un modèle d’erreur enrichi à l’aide du package Grpc.StatusProto. Ce package comporte des méthodes pour créer des modèles d’erreur enrichis sur le serveur et les faire lire par un client. Le modèle d’erreur enrichi s’appuie sur les fonctionnalités de gestion intégrées de gRPC et peut être utilisé côte à côte.

Important

Les erreurs sont incluses dans les en-têtes, dont le total dans les réponses est souvent limité à 8 Ko (8 192 octets). Vérifiez que les en-têtes contenant des erreurs ne dépassent pas 8 Ko.

Création d’erreurs enrichies sur le serveur

Les erreurs enrichies sont créées à partir de Google.Rpc.Status. Ce type est différent de Grpc.Core.Status.

Google.Rpc.Status contient des champs d’état, de message et de détails. Le champ le plus important est le champ de détails, qui est un champ répétitif des valeurs Any. Les détails sont l’emplacement où des charges utiles complexes sont ajoutées.

Bien que tout type de message puisse être utilisé comme charge utile, il est recommandé d’utiliser l’une des charges utiles d’erreur standard :

  • BadRequest
  • PreconditionFailure
  • ErrorInfo
  • ResourceInfo
  • QuotaFailure

Grpc.StatusProto inclut la ToRpcException, une méthode d’assistance pour convertir Google.Rpc.Status en erreur. Levez l’erreur à partir de la méthode de serveur gRPC :

public class GreeterService : Greeter.GreeterBase
{
    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        ArgumentNotNullOrEmpty(request.Name);

        return Task.FromResult(new HelloReply { Message = "Hello " + request.Name });
    }
    
    public static void ArgumentNotNullOrEmpty(string value, [CallerArgumentExpression(nameof(value))] string? paramName = null)
    {
        if (string.IsNullOrEmpty(value))
        {
            var status = new Google.Rpc.Status
            {
                Code = (int)Code.InvalidArgument,
                Message = "Bad request",
                Details =
                {
                    Any.Pack(new BadRequest
                    {
                        FieldViolations =
                        {
                            new BadRequest.Types.FieldViolation { Field = paramName, Description = "Value is null or empty" }
                        }
                    })
                }
            };
            throw status.ToRpcException();
        }
    }
}

Lecture d’erreurs enrichies par un client

Les erreurs enrichies sont lues à partir de la RpcException interceptée dans le client. Interceptez l’exception et utilisez les méthodes d’assistance fournies par Grpc.StatusCode pour obtenir son instance Google.Rpc.Status :

var client = new Greet.GreeterClient(channel);

try
{
    var reply = await client.SayHelloAsync(new HelloRequest { Name = name });
    Console.WriteLine("Greeting: " + reply.Message);
}
catch (RpcException ex)
{
    Console.WriteLine($"Server error: {ex.Status.Detail}");
    var badRequest = ex.GetRpcStatus()?.GetDetail<BadRequest>();
    if (badRequest != null)
    {
        foreach (var fieldViolation in badRequest.FieldViolations)
        {
            Console.WriteLine($"Field: {fieldViolation.Field}");
            Console.WriteLine($"Description: {fieldViolation.Description}");
        }
    }
}

Le code précédent :

  • Effectue un appel gRPC à l’intérieur d’un try/catch qui intercepte RpcException.
  • Appelle GetRpcStatus() pour tenter d’obtenir le modèle d’erreur enrichi à partir de l’exception.
  • Appelle GetDetail<BadRequest>() pour tenter d’obtenir une charge utile BadRequest à partir de l’erreur enrichie.

Ressources supplémentaires