[GO] Rückgabe von Fehlerdetails bei Fehler mit grpc-node

Rückgabe von Fehlerdetails bei Fehler mit grpc-node

Wenn Sie Node als Backend von gprc auswählen, wird der offizielle grpc-Node als Framework ausgewählt. https://github.com/grpc/grpc-node

Das normale System kann reibungslos implementiert werden, aber ich hatte es schwer, als ich zum Zeitpunkt des Fehlers Fehlerdetails in Go usw. verwenden wollte, so dass Memorandum (Ich weiß nicht, ob es genau errdetails heißt, aber ich nenne es, weil Go's Paketname errdetails ist)

Was sind Fehlerdetails?

Ein Nachrichtenobjekt, das die Details des vom gRPC-Framework bereitgestellten Fehlers beschreiben kann. Zum Beispiel denke ich, dass es beim Entwerfen einer REST-API notwendig ist, auch ein JSON-Fehlerobjekt zu entwerfen. Es gibt kein einheitliches Format (es gibt einen Standard namens RFC7807, der jedoch nur selten befolgt wird), und es wird erwartet, dass er zum Zeitpunkt des Entwurfs verwirrt wird.

Durch die Verwendung des von gRPC bereitgestellten Nachrichtenobjekts und -mechanismus können Sie daher eine Erschöpfung zum Zeitpunkt des Entwurfs vermeiden.

Im folgenden Implementierungsbeispiel lautet der Status "InvalidArgument" und das Feld "Name" kann einen Fehler mit den erforderlichen Informationen zurückgeben. Es ist wie "HTTP-Statuscode = 400 und json von Fehlerdetails wurden zusammen zurückgegeben "in REST. (Es kann falsch sein, weil es ein Beispielcode ist)

Serverseite


sts := status.New(codes.InvalidArgument, "validation error")
badRequest := &errdetails.BadRequest{
    FieldViolations: []*errdetails.BadRequest_FieldViolation{
        {
            Field:       "name",
            Description: "Must not be null",
        },
    },
}
details, _ := sts.WithDetails(badRequest)
err := details.Err() //Wenn Sie diesen Fehler endgültig zurückgeben, funktioniert das Framework einwandfrei

Client-Seite


...
_, err := client.SendMessage(context.Background(), req)
sts := status.Convert(err)
for _, detail := range st.Details() {
    switch ds := detail.(type) {
    case *errdetails.BadRequest:
        fmt.Println(len(ds)) // <=Länge "1"
        fmt.Println(ds[0].Field) // <=Feldname"
        fmt.Println(ds[1].Description) // <=Nachrichtendetails "Darf nicht null sein"
    case default:
        fmt.Println("Other error detail")
    }
}

Der tatsächliche Status dieses Nachrichtenobjekts ist nicht die Spezifikation, die gRPC selbst hat, sondern die vorkompilierte Protodatei der von Google bereitgestellten Voreinstellung. Dieses Mal habe ich die Bad Request-Nachricht ausprobiert, aber es gibt viele andere https://godoc.org/google.golang.org/genproto/googleapis/rpc/errdetails

Es gibt auch andere kompilierte Protodateien wie den leeren Typ. Das gRPC-Framework von Go wird als Protokoll geliefert, sodass Sie es im Fall der Go-Sprache verwenden können, ohne etwas zu tun.

Der Mechanismus dieser Fehlermeldung selbst ist einfach. Wenn ein Fehler auftritt, wird diese Meldung einfach in den Trailer von gRPC gestellt.

Ich habe es tatsächlich mit Node versucht

Früher war die Go-Sprache bereits im Framework enthalten, aber was ist mit dem Knoten? Abschließend ist es nicht enthalten und muss mitgebracht werden

Es ist ein bisschen unfreundlich, aber ich leihe mir die Errdetails-Protodatei aus dem Google Apis-Repository aus und protokolliere sie. https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto

Nachdem ich ein Nachrichtenobjekt habe, werde ich den Fehler tatsächlich in den Trailer einfügen und zurückgeben. Sie benötigen einen Schlüssel, um eine Nachricht auf Trailer zu setzen. Lassen Sie uns also herausfinden, um welchen Schlüssel es sich handelt Bei der Untersuchung der Implementierung von gRPC auf der Go-Seite scheint das Objekt unter dem Schlüsselnamen "grpc-status-details-bin" gespeichert zu sein.

Wenn Sie den Fehler das nächste Mal in den Trailer packen, müssen Sie den gRPC-Typ Any verwenden. https://developers.google.com/protocol-buffers/docs/proto3#any

Der Any-Typ ist eine Art Sprungwerkzeugtyp, der jedes gRPC-Objekt packen kann. Es ist jedoch gut, gRPC-Objekte in einen beliebigen Typ zu packen, es ist jedoch erforderlich, zum Zeitpunkt der Serialisierung und Deserialisierung einen Typ anzugeben. Zum Beispiel scheint "BadRequest" den Modellnamen "google.rpc.BadRequest" zu haben. Ist es in Ordnung, den Paketnamen und die Nachricht mit "." Zu verbinden und den Modellnamen mit dem vollständigen Namen anzugeben?

Wenn Sie versuchen, es mit TypeScript zu implementieren, wird wie folgt vorgegangen (Es kann falsch sein, weil es ein Beispielcode ist)

Serverseite


import { Metadata, ServiceError, status } from 'grpc'
import { BadRequest } from '../proto/google/error_details_pb'
import { Status as RpcStatus } from '../proto/google/status_pb'

const badRequest = new BadRequest()
const fieldViolation = new BadRequest.FieldViolation()
fieldViolation.setField('name')
fieldViolation.setDescription('Must not be null')
badRequest.addFieldViolations(fieldViolation)

const [metadata, rpcStatus, detail] = [new Metadata(), new RpcStatus(), new Any()]
detail.pack(badRequest.serializeBinary(), 'google.rpc.BadRequest') //Pack Beliebiger Typ
rpcStatus.setCode(status.INVALID_ARGUMENT)
rpcStatus.setMessage('validation error')
rpcStatus.addDetails(detail)
metadata.set('grpc-status-details-bin', Buffer.from(status.serializeBinary()))

const serviceError: ServiceError = { code, message, name, metadata } //Wenn Sie diesen Fehler endgültig zurückgeben, funktioniert das Framework einwandfrei

Client-Seite


import { BadRequest } from "../proto/google/error_details_pb"
import { Status as RpcStatus } from '../proto/google/status_pb'

...

const buffer = error.metadata.get("grpc-status-details-bin")[0] // <=Dieser Fehler ist grpc-Fehler beim Zurückgeben vom Client
status = Status.deserializeBinary(buffer)
detail = status.getDetailsList()[0]

//Diesmal`BadRequest`Ich habe mich für den Typ entschieden, aber in Wirklichkeit habe ich mich für den Typ entschieden, der mit fehlerhaften Details eingegeben werden kann`detail.getTypeName()`Müssen damit umgehen
const badRequest = detail.unpack(BadRequest.deserializeBinary, detail.getTypeName()) //Packen Sie jeden Typ aus

Auf diese Weise können Sie Errdtails zwischen der Go-Sprache und dem Knoten austauschen.

Andere

Im obigen Beispiel wurde es auch auf der Clientseite manuell deserialisiert. Wenn Sie jedoch Fehlerdetails erhalten, gibt es eine Bibliothek wie die folgende, sodass die Verwendung nicht schlecht ist https://github.com/stackpath/node-grpc-error-details Ich habe auch über die obige Implementierung nachgedacht, indem ich mich auf diese Bibliothek bezogen habe. Dies ist hilfreich, da hier auch beschrieben wird, wie der Typname mit "detail.getTypeName ()" behandelt wird.

Übrigens scheint es ein Problem zu geben, bei dem ich möchte, dass Fehlerdetails in grpc-node offiziell sind (ich habe diese Bibliothek hier gefunden). https://github.com/grpc/grpc-node/issues/184

errdetails ist praktisch, weil es Ihnen die Mühe beim Entwerfen erspart, aber ich fand es schmerzhaft, dass es nicht enthalten war, aber es war je nach Sprache unterschiedlich. Wenn Sie sich also mit Fehlerdetails befassen, wenn diese nicht in anderen Sprachen gebündelt sind, oder wenn Sie die nicht offiziell unterstützte gRPC-Bibliothek verwenden, sollten Sie in der Lage sein, diesen Inhalt in anderen Sprachen zu lokalisieren.

Wie oben erwähnt, ist die Verwendung von Errdetails mit grpc-node ziemlich ärgerlich, aber In letzter Zeit nimmt die Kombination von gRPC + GraphQL zu, und wenn Sie Apollo Server auf der GraphQL-Seite verwenden möchten, halte ich grpc-node für nützlich.

Ich dachte noch einmal, dass die Go-Sprache im gRPC-Bereich bevorzugt behandelt wird.

Recommended Posts

Rückgabe von Fehlerdetails bei Fehler mit grpc-node
Fehler bei der Pip-Installation
Methodenkette mit `return self`
Kontrollieren Sie die Fehlerformulierung mit Nginx
Fehler beim Spielen mit Python
Bloggen mit Pelican unter Windows
Umgang mit PermissionError [Fehler 1] von pip install -U pip unter macOS Sierra
Fehler und Lösung bei der Installation von Python3 mit Homebrew auf einem Mac (Catalina 10.15)
Wenn ich eine Fehlermeldung mit Pylint unter Windows Atom erhalte