Condividi tramite


Gestione degli errori con gRPC

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Avviso

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Di James Newton-King

Questo articolo illustra la gestione degli errori e gRPC:

  • Funzionalità di gestione degli errori predefinite che usano i codici di stato gRPC e i messaggi di errore.
  • Invio di informazioni di errore complesse e strutturate tramite la gestione degli errori avanzata.

Gestione degli errori predefinita

Le chiamate gRPC comunicano l'esito positivo o negativo con un codice di stato. Quando una chiamata gRPC viene completata correttamente, il server restituisce OK uno stato al client. Se si verifica un errore, gRPC restituisce:

  • Codice di stato dell'errore, ad esempio CANCELLED o UNAVAILABLE.
  • Messaggio di errore di stringa facoltativo.

I tipi comunemente usati con la gestione degli errori sono:

  • StatusCode: enumerazione dei codici di stato gRPC. OK segnali di successo; altri valori sono un errore.
  • Status: oggetto struct che combina un StatusCode e un messaggio di errore di stringa facoltativo. Il messaggio di errore fornisce altri dettagli su ciò che è accaduto.
  • RpcException: tipo di eccezione con Status valore. Questa eccezione viene generata nei metodi del server gRPC e rilevata dai client gRPC.

La gestione degli errori predefinita supporta solo un codice di stato e una descrizione stringa. Per inviare informazioni di errore complesse dal server al client, usare la gestione degli errori avanzata.

Generare errori del server

Una chiamata al server gRPC restituisce sempre uno stato. Il server restituisce OK automaticamente quando un metodo viene completato correttamente.

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

Il codice precedente:

  • Implementa il metodo unario SayHello che viene completato correttamente quando restituisce un messaggio di risposta.
  • Implementa il metodo di streaming SayHelloStreaming del server che viene completato correttamente al termine del metodo .

Stato dell'errore del server

I metodi gRPC restituiscono un codice di stato di errore generando un'eccezione. Quando viene generata un'eccezione RpcException nel server, il codice di stato e la descrizione vengono restituiti al 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}" });
    }
}

I tipi di eccezione generati non RpcException causano anche l'esito negativo della chiamata, ma con un UNKNOWN codice di stato e un messaggio generico Exception was thrown by handler.

Exception was thrown by handler viene inviato al client anziché al messaggio di eccezione per impedire l'esposizione di informazioni potenzialmente riservate. Per visualizzare un messaggio di errore più descrittivo in un ambiente di sviluppo, configurare EnableDetailedErrors.

Gestire gli errori del client

Quando un client gRPC effettua una chiamata, il codice di stato viene convalidato automaticamente quando si accede alla risposta. Ad esempio, in attesa di una chiamata gRPC unaria restituisce il messaggio inviato dal server se la chiamata ha esito positivo e genera un'eccezione RpcException in caso di errore. Intercettare RpcException per gestire gli errori in 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);
}

Il codice precedente:

  • Effettua una chiamata gRPC unaria al SayHello metodo .
  • Scrive il messaggio di risposta nella console se ha esito positivo.
  • RpcException Rileva e scrive i dettagli dell'errore in caso di errore.

Scenari di errore

Gli errori sono rappresentati da RpcException con un codice di stato di errore e un messaggio di dettaglio facoltativo. RpcException viene generata in molti scenari:

  • La chiamata non è riuscita nel server e il server ha inviato un codice di stato di errore. Ad esempio, il client gRPC ha avviato una chiamata senza dati necessari dal messaggio di richiesta e il server restituisce un INVALID_ARGUMENT codice di stato.
  • Si è verificato un errore all'interno del client durante l'esecuzione della chiamata gRPC. Ad esempio, un client effettua una chiamata gRPC, non può connettersi al server e genera un errore con lo stato UNAVAILABLE.
  • L'oggetto CancellationToken passato alla chiamata gRPC viene annullato. La chiamata gRPC viene arrestata e il client genera un errore con stato CANCELLED.
  • Una chiamata gRPC supera la scadenza configurata. La chiamata gRPC viene arrestata e il client genera un errore con stato DEADLINE_EXCEEDED.

Gestione degli errori avanzata

La gestione avanzata degli errori consente l'invio di informazioni complesse e strutturate con messaggi di errore. Ad esempio, la convalida dei campi messaggio in arrivo che restituisce un elenco di nomi di campo e descrizioni non validi. Il google.rpc.Status modello di errore viene spesso usato per inviare informazioni di errore complesse tra app gRPC.

gRPC in .NET supporta un modello di errore avanzato usando il Grpc.StatusProto pacchetto. Questo pacchetto include metodi per la creazione di modelli di errore avanzati nel server e la lettura da parte di un client. Il modello di errore avanzato si basa sulle funzionalità di gestione predefinite di gRPC e possono essere usate side-by-side.

Importante

Gli errori sono inclusi nelle intestazioni e le intestazioni totali nelle risposte sono spesso limitate a 8 KB (8.192 byte). Assicurarsi che le intestazioni contenenti errori non superino 8 KB.

Creazione di errori avanzati nel server

Gli errori avanzati vengono creati da Google.Rpc.Status. Questo tipo è diverso da Grpc.Core.Status.

Google.Rpc.Status include campi di stato, messaggio e dettagli. Il campo più importante è costituito dai dettagli, ovvero un campo ripetuto di Any valori. I dettagli sono la posizione in cui vengono aggiunti payload complessi.

Anche se qualsiasi tipo di messaggio può essere usato come payload, è consigliabile usare uno dei payload di errore standard:

  • BadRequest
  • PreconditionFailure
  • ErrorInfo
  • ResourceInfo
  • QuotaFailure

Grpc.StatusProto include il ToRpcException metodo helper da convertire Google.Rpc.Status in un errore. Generare l'errore dal metodo del server 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();
        }
    }
}

Lettura di errori avanzati da parte di un client

Gli errori avanzati vengono letti dal RpcException rilevato nel client. Intercettare l'eccezione e usare i metodi helper forniti da Grpc.StatusCode per ottenere la relativa Google.Rpc.Status istanza:

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

Il codice precedente:

  • Effettua una chiamata gRPC all'interno di un try/catch che intercetta RpcException.
  • Chiama GetRpcStatus() per tentare di ottenere il modello di errore avanzato dall'eccezione.
  • Chiama GetDetail<BadRequest>() per tentare di ottenere un BadRequest payload dall'errore avanzato.

Risorse aggiuntive