Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.
Resolver
In den vorherigen Abschnitten haben Sie mehr über die Komponenten des Schemas und der Datenquelle erfahren. Jetzt müssen wir uns damit befassen, wie das Schema und die Datenquellen interagieren. Alles beginnt mit dem Resolver.
Ein Resolver ist eine Codeeinheit, die festlegt, wie die Daten dieses Felds aufgelöst werden, wenn eine Anfrage an den Dienst gestellt wird. Resolver sind an bestimmte Felder innerhalb Ihrer Typen in Ihrem Schema angehängt. Sie werden am häufigsten verwendet, um die Zustandsänderungsoperationen für Ihre Abfrage-, Mutations- und Abonnementfeldoperationen zu implementieren. Der Resolver verarbeitet die Anfrage eines Clients und gibt dann das Ergebnis zurück. Dabei kann es sich um eine Gruppe von Ausgabetypen wie Objekte oder Skalare handeln:
Laufzeit des Resolvers
AWS AppSync In müssen Sie zunächst eine Laufzeit für Ihren Resolver angeben. Eine Resolver-Laufzeit gibt die Umgebung an, in der ein Resolver ausgeführt wird. Sie bestimmt auch die Sprache, in der Ihre Resolver geschrieben werden. AWS AppSync unterstützt derzeit APPSYNC_JS für JavaScript und Velocity Template Language (VTL). Siehe JavaScript Laufzeitfunktionen für Resolver und Funktionen für JavaScript oder Referenz zum Resolver-Mapping-Template-Tool für VTL.
Struktur des Resolvers
Code-wise, Resolver können auf verschiedene Arten strukturiert werden. Es gibt Unit - und Pipeline-Resolver.
Resolver für Einheiten
Ein Unit-Resolver besteht aus Code, der einen einzelnen Anforderungs- und Antworthandler definiert, die für eine Datenquelle ausgeführt werden. Der Anforderungshandler verwendet ein Kontextobjekt als Argument und gibt die Anforderungsnutzlast zurück, die zum Aufrufen Ihrer Datenquelle verwendet wurde. Der Antworthandler erhält eine Nutzlast von der Datenquelle mit dem Ergebnis der ausgeführten Anfrage zurück. Der Response-Handler wandelt die Nutzlast in eine GraphQL-Antwort um, um das GraphQL-Feld aufzulösen.
Pipeline-Resolver
Bei der Implementierung von Pipeline-Resolvern gibt es eine allgemeine Struktur, der sie folgen:
-
Vor dem Schritt: Wenn der Client eine Anfrage stellt, werden den Resolvern für die verwendeten Schemafelder (in der Regel Ihre Abfragen, Mutationen, Abonnements) die Anforderungsdaten übergeben. Der Resolver beginnt mit der Verarbeitung der Anforderungsdaten mit einem Before-Step-Handler, der es ermöglicht, einige Vorverarbeitungsvorgänge durchzuführen, bevor die Daten den Resolver durchlaufen.
-
Funktion (en): Nach der Ausführung des Before-Schritts wird die Anforderung an die Funktionsliste übergeben. Die erste Funktion in der Liste wird für die Datenquelle ausgeführt. Eine Funktion ist eine Teilmenge des Codes Ihres Resolvers, die einen eigenen Anfrage- und Antworthandler enthält. Ein Request-Handler nimmt die Anforderungsdaten und führt Operationen mit der Datenquelle durch. Der Antworthandler verarbeitet die Antwort der Datenquelle, bevor er sie an die Liste zurückgibt. Wenn es mehr als eine Funktion gibt, werden die Anforderungsdaten an die nächste Funktion in der Liste gesendet, die ausgeführt werden soll. Die Funktionen in der Liste werden seriell in der vom Entwickler definierten Reihenfolge ausgeführt. Sobald alle Funktionen ausgeführt wurden, wird das Endergebnis an den Nachschritt übergeben.
-
Nachschritt: Der Nachschritt ist eine Handler-Funktion, mit der Sie einige letzte Operationen an der Antwort der endgültigen Funktion ausführen können, bevor Sie sie an die GraphQL-Antwort übergeben.
Struktur des Resolver-Handlers
Handler sind in der Regel Funktionen, die aufgerufen Request werden und: Response
export function request(ctx) { // Code goes here } export function response(ctx) { // Code goes here }
In einem Unit-Resolver wird es nur einen Satz dieser Funktionen geben. In einem Pipeline-Resolver gibt es einen Satz dieser Funktionen für den Vorher- und Nachher-Schritt sowie einen zusätzlichen Satz pro Funktion. Um zu visualisieren, wie das aussehen könnte, schauen wir uns einen einfachen Query Typ an:
type Query { helloWorld: String! }
Dies ist eine einfache Abfrage mit einem Feld namens helloWorld typeString. Nehmen wir an, wir möchten immer, dass dieses Feld die Zeichenfolge „Hello World“ zurückgibt. Um dieses Verhalten zu implementieren, müssen wir den Resolver zu diesem Feld hinzufügen. In einem Unit-Resolver könnten wir etwas wie das Folgende hinzufügen:
export function request(ctx) { return {} } export function response(ctx) { return "Hello World" }
Das request kann einfach leer gelassen werden, da wir keine Daten anfordern oder verarbeiten. Wir können auch davon ausgehen, dass es sich bei unserer Datenquelle um eine handeltNone, was darauf hinweist, dass dieser Code keine Aufrufe ausführen muss. Die Antwort gibt einfach „Hello World“ zurück. Um diesen Resolver zu testen, müssen wir eine Anfrage mit dem Abfragetyp stellen:
query helloWorldTest { helloWorld }
Dies ist eine Abfrage namenshelloWorldTest, die das helloWorld Feld zurückgibt. Bei der Ausführung wird der helloWorld Feldresolver ebenfalls ausgeführt und gibt die Antwort zurück:
{ "data": { "helloWorld": "Hello World" } }
Die Rückgabe solcher Konstanten ist das Einfachste, was Sie tun können. In Wirklichkeit werden Sie Eingaben, Listen und mehr zurückgeben. Hier ist ein komplizierteres Beispiel:
type Book { id: ID! title: String } type Query { getBooks: [Book] }
Hier geben wir eine Liste von zurückBooks. Nehmen wir an, wir verwenden eine DynamoDB-Tabelle zum Speichern von Buchdaten. Unsere Handler könnten so aussehen:
/** * Performs a scan on the dynamodb data source */ export function request(ctx) { return { operation: 'Scan' }; } /** * return a list of scanned post items */ export function response(ctx) { return ctx.result.items; }
Unsere Anfrage verwendete einen eingebauten Scanvorgang, um nach allen Einträgen in der Tabelle zu suchen, die Ergebnisse im Kontext zu speichern und sie dann an die Antwort weiterzuleiten. Die Antwort nahm die Ergebniselemente und gab sie in der Antwort zurück:
{ "data": { "getBooks": { "items": [ { "id": "abcdefgh-1234-1234-1234-abcdefghijkl", "title": "book1" }, { "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "title": "book2" }, ... ] } } }
Resolver-Kontext
In einem Resolver muss jeder Schritt in der Kette von Handlern den Status der Daten aus den vorherigen Schritten kennen. Das Ergebnis eines Handlers kann gespeichert und als Argument an einen anderen übergeben werden. GraphQL definiert vier grundlegende Resolver-Argumente:
| Resolver-Basisargumente | Description |
|---|---|
obj, root, parent, usw. |
Das Ergebnis des übergeordneten Elements. |
args |
Die Argumente, die für das Feld in der GraphQL-Abfrage bereitgestellt wurden. |
context |
Ein Wert, der jedem Resolver zur Verfügung gestellt wird und wichtige Kontextinformationen wie den aktuell angemeldeten Benutzer oder den Zugriff auf eine Datenbank enthält. |
info |
Ein Wert, der feldspezifische Informationen enthält, die für die aktuelle Abfrage relevant sind, sowie die Schemadetails. |
AWS AppSync In kann das Argument context (ctx) alle oben genannten Daten enthalten. Es ist ein Objekt, das pro Anfrage erstellt wird und Daten wie Autorisierungsdaten, Ergebnisdaten, Fehler, Anforderungsmetadaten usw. enthält. Der Kontext ist eine einfache Möglichkeit für Programmierer, Daten zu bearbeiten, die aus anderen Teilen der Anfrage stammen. Nimm diesen Ausschnitt noch einmal:
/** * Performs a scan on the dynamodb data source */ export function request(ctx) { return { operation: 'Scan' }; } /** * return a list of scanned post items */ export function response(ctx) { return ctx.result.items; }
Der Anfrage wird der Kontext (ctx) als Argument gegeben; dies ist der Status der Anfrage. Es führt einen Scan für alle Elemente in einer Tabelle durch und speichert das Ergebnis dann wieder im Kontext inresult. Der Kontext wird dann an das Antwortargument übergeben, das auf das zugreift result und dessen Inhalt zurückgibt.
Anfragen und Parsen
Wenn Sie eine Abfrage an Ihren GraphQL-Dienst stellen, muss dieser einen Analyse- und Validierungsprozess durchlaufen, bevor er ausgeführt wird. Ihre Anfrage wird analysiert und in einen abstrakten Syntaxbaum übersetzt. Der Inhalt des Baums wird validiert, indem mehrere Validierungsalgorithmen anhand Ihres Schemas ausgeführt werden. Nach dem Validierungsschritt werden die Knoten des Baums durchquert und verarbeitet. Resolver werden aufgerufen, die Ergebnisse werden im Kontext gespeichert und die Antwort wird zurückgegeben. Betrachten Sie beispielsweise diese Abfrage:
query { Person { //object type name //scalar age //scalar } }
Wir kehren Person mit den Feldern a name und age zurück. Wenn Sie diese Abfrage ausführen, sieht der Baum ungefähr so aus:
Aus dem Baum geht hervor, dass diese Anfrage den Stamm Query im Schema nach dem durchsucht. Innerhalb der Abfrage wird das Person Feld aufgelöst. Aus früheren Beispielen wissen wir, dass dies eine Eingabe des Benutzers sein könnte, eine Liste von Werten usw., die höchstwahrscheinlich an einen Objekttyp gebunden Person ist, der die Felder enthält, die wir benötigen (nameundage). Sobald diese beiden untergeordneten Felder gefunden wurden, werden sie in der angegebenen Reihenfolge (namegefolgt vonage) aufgelöst. Sobald der Baum vollständig aufgelöst ist, ist die Anfrage abgeschlossen und wird an den Client zurückgesendet.