Teilen über


Fehlerbehandlung mit gRPC

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Warnung

Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der Supportrichtlinie für .NET und .NET Core. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Von James Newton-King

In diesem Artikel erfahren Sie mehr über Fehlerbehandlung und gRPC:

  • Integrierte Fehlerbehandlungsfunktionen mit gRPC-Statuscodes und -Fehlermeldungen.
  • Senden komplexer, strukturierter Fehlerinformationen mithilfe umfassender Fehlerbehandlung.

Integrierte Fehlerbehandlung

gRPC-Aufrufe teilen Erfolg oder Fehler mit einem Statuscode mit. Wenn ein gRPC-Aufruf erfolgreich abgeschlossen wird, gibt der Server den Status OK an den Client zurück. Wenn ein Fehler auftritt, gibt gRPC Folgendes zurück:

  • Einen Fehlerstatuscode, z. B. CANCELLED oder UNAVAILABLE.
  • Eine optionale Zeichenfolgenfehlermeldung.

Bei der Fehlerbehandlung werden häufig folgende Typen verwendet:

  • StatusCode: Eine Enumeration von gRPC-Statuscodes. OK signalisiert Erfolg. Andere Werte stehen für einen Fehler.
  • Status: Ein struct-Element, das StatusCode und eine optionale Zeichenfolgenfehlermeldung kombiniert. Die Fehlermeldung enthält weitere Details dazu, was passiert ist.
  • RpcException: Ein Ausnahmetyp mit einem Status-Wert. Diese Ausnahme wird in gRPC-Servermethoden ausgelöst und von gRPC-Clients abgefangen.

Die integrierte Fehlerbehandlung unterstützt nur einen Statuscode und eine Zeichenfolgenbeschreibung. Verwenden Sie die umfassende Fehlerbehandlung, um komplexe Fehlerinformationen vom Server an den Client zu senden.

Auslösen von Serverfehlern

Ein gRPC-Serveraufruf gibt immer einen Status zurück. Der Server gibt automatisch OK zurück, wenn eine Methode erfolgreich abgeschlossen wurde.

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

Der vorangehende Code:

  • Implementiert die unäre SayHello-Methode, die beim Zurückgeben einer Antwort erfolgreich abgeschlossen wird.
  • Implementiert die Serverstreamingmethode SayHelloStreaming, die nach Ausführung der Methode erfolgreich abgeschlossen wird.

Serverfehlerstatus

gRPC-Methoden geben einen Fehlerstatuscode zurück, indem eine Ausnahme ausgelöst wird. Wenn RpcException auf dem Server ausgelöst wird, werden der Statuscode und die Beschreibung an den Client zurückgegeben:

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

Ausgelöste Ausnahmetypen, die nicht vom Typ RpcException sind, können ebenfalls zu einem fehlerhaften Aufruf führen, allerdings werden der Statuscode UNKNOWN und die generische Meldung Exception was thrown by handler zurückgegeben.

Anstelle der Ausnahmemeldung wird Exception was thrown by handler an den Client gesendet, um zu verhindern, dass potenziell vertrauliche Informationen verfügbar gemacht werden. Um eine aussagekräftigere Fehlermeldung in einer Entwicklungsumgebung anzuzeigen, konfigurieren Sie EnableDetailedErrors.

Behandeln von Clientfehlern

Wenn ein gRPC-Client einen Aufruf ausführt, wird der Statuscode beim Zugriff auf die Antwort automatisch überprüft. Beim Warten auf einen unären gRPC-Aufruf wird beispielsweise die vom Server gesendete Nachricht zurückgegeben, wenn der Aufruf erfolgreich ist. Bei einem Fehler wird RpcException ausgelöst. Abfangen von RpcException zum Behandeln von Fehlern in einem 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);
}

Der vorangehende Code:

  • Führt einen unären gRPC-Aufruf der SayHello-Methode aus.
  • Schreibt die Antwortnachricht in die Konsole, wenn der Vorgang erfolgreich war.
  • Fängt RpcException ab und gibt bei einem Fehler die Fehlerdetails aus.

Fehlerszenarios

Fehler werden durch RpcException mit einem Fehlerstatuscode und einer optionalen Detailmeldung angegeben. RpcException wird in vielen Szenarien ausgelöst:

  • Beim Aufruf auf dem Server ist ein Fehler aufgetreten, und der Server hat einen Fehlerstatuscode gesendet. Beispielsweise hat der gRPC-Client einen Aufruf gestartet, bei dem erforderliche Daten aus der Anforderungsmeldung fehlen, und der Server gibt den Statuscode INVALID_ARGUMENT zurück.
  • Beim Ausführen des gRPC-Aufrufs ist ein Fehler innerhalb des Clients aufgetreten. Beispielsweise führt ein Client einen gRPC-Aufruf aus, kann keine Verbindung mit dem Server herstellen und löst einen Fehler mit dem Status UNAVAILABLE aus.
  • Das an den gRPC-Aufruf übergebene Abbruchtoken (CancellationToken) wird abgebrochen. Der gRPC-Aufruf wird beendet, und der Client löst einen Fehler mit dem Status CANCELLED aus.
  • Ein gRPC-Aufruf überschreitet den konfigurierten Stichtag. Der gRPC-Aufruf wird beendet, und der Client löst einen Fehler mit dem Status DEADLINE_EXCEEDED aus.

Umfassende Fehlerbehandlung

Die umfassende Fehlerbehandlung ermöglicht das Senden komplexer, strukturierter Informationen mit Fehlermeldungen, z. B. die Überprüfung von Feldern für eingehende Nachrichten, die eine Liste ungültiger Feldnamen und Beschreibungen zurückgibt. Das google.rpc.Status-Fehlermodell wird häufig verwendet, um komplexe Fehlerinformationen zwischen gRPC-Apps zu senden.

gRPC unter .NET unterstützt ein umfangreiches Fehlermodell mithilfe des Grpc.StatusProto-Pakets. Dieses Paket verfügt über Methoden zum Erstellen umfangreicher Fehlermodelle auf dem Server und zum Lesen durch einen Client. Das umfangreiche Fehlermodell baut auf den integrierten Behandlungsfunktionen von gRPC auf und kann parallel verwendet werden.

Wichtig

Fehler sind in Headern enthalten, und die gesamten Header in Antworten sind häufig auf 8 KB (8.192 Bytes) beschränkt. Stellen Sie sicher, dass die Header, die Fehler enthalten, 8 KB nicht überschreiten.

Erstellen aussagekräftiger Fehler auf dem Server

Aussagekräftige Fehler werden von Google.Rpc.Status erstellt. Dieser Typ unterscheidet sich von Grpc.Core.Status.

Google.Rpc.Status enthält Status-, Meldungs- und Detailfelder. Das wichtigste Feld ist „Details“, bei dem es sich um ein wiederholtes Feld mit Any-Werten handelt. In den Details werden komplexe Nutzlasten hinzugefügt.

Obwohl jeder Meldungstyp als Nutzlast verwendet werden kann, empfiehlt es sich, eine der Standardfehlernutzlasten zu verwenden:

  • BadRequest
  • PreconditionFailure
  • ErrorInfo
  • ResourceInfo
  • QuotaFailure

Grpc.StatusProto enthält die Hilfsmethode ToRpcException zum Konvertieren von Google.Rpc.Status in einen Fehler. Lösen Sie den Fehler über die gRPC-Servermethode aus:

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

Lesen aussagekräftiger Fehler durch einen Client

Aussagekräftige Fehler werden aus dem im Client abgefangenen RpcException-Element gelesen. Fangen Sie die Ausnahme ab, und verwenden Sie von Grpc.StatusCode bereitgestellte Hilfsmethoden, um die Google.Rpc.Status-Instanz abzurufen:

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

Der vorangehende Code:

  • Führt einen gRPC-Aufruf innerhalb eines Try/Catch-Blocks aus, der RpcException abfängt.
  • Ruft GetRpcStatus() auf, um zu versuchen, das umfangreiche Fehlermodell aus der Ausnahme abzurufen.
  • Ruft GetDetail<BadRequest>() auf, um zu versuchen, eine BadRequest-Nutzlast aus dem aussagekräftigen Fehler abzurufen.

Zusätzliche Ressourcen