Menü

Validierung an API Grenzen: Client, Server und die Lücke dazwischen

Validierung an API Grenzen gehört auf beide Seiten: in den Client für sofortige Rückmeldung, in den Server für die maßgebliche Prüfung. Beides zu verwechseln erzeugt Meldungen, die in die Irre führen.

Veröffentlicht am

  • Validierung
  • API
  • Testdaten

Eine Nummer tritt an mehr als einer Stelle in ein Produkt ein. Sie wird in ein Formular getippt, durch einen Client weitergegeben, an einen Dienst übertragen, gespeichert und später von einer Person im Betrieb wieder ausgelesen. Jeder dieser Punkte ist eine Grenze, und jede Grenze hat einen anderen Grund, den Wert zu prüfen.

Teams, die alle diese Punkte als dieselbe Prüfung behandeln, enden mit doppelter Logik, die auseinanderdriftet, und mit dem Schlechtesten aus beiden Welten: langsame Rückmeldung, weil die maßgebliche Prüfung entfernt liegt, und schwache Autorität, weil die schnelle Prüfung die einzige ist, die tatsächlich läuft. Die Ebenen zu trennen behebt beides.

Sollte die Validierung im Client oder im Server liegen?

Beides, und zwar aus verschiedenen Gründen. Der Client ist der Ort, an dem die Latenz zählt. Wer eine lange Kennung eintippt, möchte einen falsch getippten Buchstaben sofort gemeldet bekommen, und eine Rundreise je Tastendruck ist der falsche Weg, das mitzuteilen. Einfache Regeln, die keine Abfrage brauchen und offline ausgeführt werden können, gehören hierher.

Der Server ist der Ort, an dem die Autorität zählt. Alles, was der Client behauptet, kann gefälscht, durch einen direkten Aufruf der Schnittstelle umgangen oder von einem älteren Build mit veraltetem Regelsatz erzeugt worden sein. Der Dienst muss jede Prüfung für alles wiederholen, was er annimmt — nicht weil er seinem eigenen Client misstraut, sondern weil der Client nicht Teil seiner Vertrauensgrenze ist.

Die beiden Ebenen sollten eine Spezifikation und eine Umsetzung teilen, veröffentlicht als Paket oder als erzeugtes Artefakt, damit die schnelle Prüfung und die maßgebliche Prüfung sich nicht darüber uneinig werden können, wie ein gültiger Wert aussieht. Wenn sie auseinanderdriften, zeigt sich das als Eingabe, die der Client annimmt und der Server zurückweist, und Nutzerinnen erleben das als unerklärlichen Fehlschlag am Ende eines Formulars.

Die hier erörterten Beispiele sind struktureller Art. Keine echte Kontonummer, Ausweisnummer oder Kartennummer sollte in einem Anforderungsprotokoll, einem Testdatensatz oder einer Fehlernutzlast erscheinen, und die in diesem Beitrag beschriebenen Werte sind ausschließlich veranschaulichende Formen.

Was an der Grenze geprüft wird und was durchgelassen wird

Die Grenze sollte bestätigen, was sie günstig bestätigen kann, und alles Übrige als Daten weiterreichen.

Prüfen Sie am Rand: den Zeichensatz, die Länge, die normalisierte Form und jedes veröffentlichte Prüfzeichen für ein Schema, das der Dienst kennt. Weisen Sie früh ab und mit einem konkreten Grund, denn die Alternative ist ein fehlerhafter Wert, der tiefer in ein System wandert, das über kein Vokabular verfügt, ihn zu beschreiben.

Lassen Sie durch: alles, was eine Registerabfrage benötigt, sofern der Dienst nicht in einem vertraglichen Verhältnis steht, das die Abfrage günstig macht. Eine Prüfung auf die Existenz eines Kontos an einer öffentlichen Schnittstelle ist gleichzeitig ein Angriffsvektor für Überlastung und ein Datenschutzrisiko, da sie ein Formular in ein Auskunftswerkzeug darüber verwandelt, ob ein Wert aktiv ist.

Normalisieren Sie einmal, an der Grenze, und speichern Sie die normalisierte Form als maßgeblichen Wert, während Sie das Original für die Anzeige behalten. Alles Nachgelagerte vergleicht dann eine einzige Darstellung, und die Fehlerklasse verschwindet, in der dieselbe Nummer in drei Schreibweisen auftaucht.

Fehlerantworten und ihre Wiederholungssemantik einordnen

Nicht jede Zurückweisung bedeutet dasselbe, und ein einziger Fehlercode für alle zwingt jeden Aufrufer zu raten, wie er reagieren soll.

Situation Bedeutung Wiederholbar
Fehlerhafte Eingabe Der Wert verletzt die Formatregel Nein — der Aufrufer muss andere Daten senden
Prüfzeichen passt nicht Die Form ist zulässig, die Arithmetik stimmt nicht zu Nein — aus demselben Grund
Nicht unterstütztes Schema Nichts, was der Dienst umsetzt, passt zu dieser Form Nein — es sei denn, der Dienst erweitert die Abdeckung
Zeitweise nicht verfügbar Eine Abhängigkeit, die die Prüfung braucht, ist ausgefallen oder gedrosselt Ja, mit wachsenden Wartezeiten
Ratenbegrenzt Der Aufrufer hat sein Kontingent überschritten Ja, nach dem in der Antwort angegebenen Intervall

Die ersten vier in eine allgemeine fehlerhafte Anfrage zusammenzuziehen ist der häufigste Entwurfsfehler, weil er einen dauerhaften Eingabefehler von einer vorübergehenden Störung ununterscheidbar macht. Clients wiederholen dann die falschen Fehlschläge oder geben die auf, die erfolgreich gewesen wären.

Geben Sie einen stabilen maschinenlesbaren Code neben einer für Menschen lesbaren Meldung zurück und dokumentieren Sie, welche Codes wiederholbar sind. Behandeln Sie diese Einordnung als Teil des Schnittstellenvertrags: Sie später zu ändern ist eine brechende Änderung für alle, die ihre Wiederholungslogik daran ausgerichtet haben.

Warum darf eine gescheiterte Prüfung nicht als fehlende Nummer gemeldet werden?

Weil die beiden Aussagen verschiedene Wahrheitsbedingungen haben. Ein Prüfzeichen, das nicht passt, besagt, dass die Zeichenkette mit sich selbst nicht übereinstimmt, was der Dienst mit Sicherheit weiß. Eine fehlende Nummer besagt, dass kein solches Konto und kein solcher Datensatz existiert, was der Dienst meist überhaupt nicht wissen kann.

Die Verwechslung richtet in beide Richtungen echten Schaden an. Wer erfährt, dass eine Nummer nicht existiert, obwohl er einen echten Wert besitzt, gibt möglicherweise eine rechtmäßige Transaktion auf oder trägt etwas anderes ein. Wer erfährt, dass eine Nummer gültig ist, weil eine Prüfung bestanden wurde, glaubt womöglich, ein Konto sei bestätigt, während lediglich die Arithmetik bestätigt wurde.

Die richtige Meldung beschreibt die Zeichenkette: Die Form passte, oder die Prüfziffer hielt nicht, oder keine umgesetzte Regel erkennt die Eingabe. Nichts in dieser Liste behauptet etwas über die Welt, und jeder Punkt teilt dem Aufrufer etwas mit, auf das er reagieren kann. Dieselbe Disziplin gilt innerhalb von Massenläufen, wo eine falsch benannte Kategorie jemanden auf die Suche nach einem Defekt schicken kann, den es nicht gibt; die Kategorien für die Massenvalidierung bauen genau auf dieser Unterscheidung auf.

Ratenbegrenzungen, Zeitüberschreitungen und Rückfallwege bei externen Abfragen

Sobald eine Prüfung Daten von außerhalb des Prozesses benötigt, übernimmt sie die Fehlerarten eines Netzaufrufs. Für sie zu entwerfen ist kein Pessimismus; es ist der Unterschied zwischen einer Verschlechterung und einem Ausfall.

Jeder externe Aufruf braucht eine Zeitüberschreitung, die kürzer ist als die Anfrage, die ihn enthält, damit eine langsame Abhängigkeit nicht das gesamte Zeitbudget verbraucht. Er braucht eine Wiederholungsregel mit wachsenden Wartezeiten und Streuung für die vorübergehenden Fehler sowie eine Schutzschaltung, damit eine dauerhaft fehlschlagende Abhängigkeit nicht weiter mit voller Rate aufgerufen wird.

Er braucht außerdem ein festgelegtes Verhalten für den Fall, dass die Abfrage nicht stattfinden kann. Zwei Antworten sind legitim, und die Wahl ist eine Produktentscheidung: die Anfrage scheitern lassen oder den Wert vorläufig annehmen und als unbestätigt kennzeichnen. Nicht legitim ist es, eine unerreichbare Abfrage stillschweigend als bestanden zu behandeln, denn das verwandelt einen Ausfall in ein Datenqualitätsproblem.

Zwischenspeichern hilft und braucht eigene Regeln. Speichern Sie nur, was der Vertrag erlaubt, bilden Sie den Schlüssel aus dem normalisierten Wert, und geben Sie den Einträgen eine Lebensdauer, die dazu passt, wie schnell sich die zugrunde liegende Tatsache ändern kann. Speichern Sie eine Zurückweisung niemals so, als wäre sie eine bestätigte Tatsache über die Nummer.

Für Entwickler: Verträge, Versionen und Protokolle

Behandeln Sie den Regelsatz als versionierte Abhängigkeit der Schnittstelle und nicht als Umsetzungsdetail.

Geben Sie an, welche Version des Regelsatzes ein Urteil erzeugt hat, damit ein Aufrufer erkennen kann, ob eine Verhaltensänderung aus seinem eigenen Build oder aus dem Dienst stammt. Versionieren Sie den Regelsatz unabhängig von der Schnittstelle, wenn sich die Abdeckung ändert, und halten Sie ältere Versionen lange genug verfügbar, dass Clients umziehen können. Veröffentlicht ein Schema neue Parameter, sollte die Änderung eine Datenaktualisierung mit neuer Versionsnummer sein und keine Codeänderung, deren Versionshinweise den Verhaltensunterschied auslassen.

Protokollieren Sie Urteile, niemals vollständige Werte. Halten Sie das Schema, das Urteil, die Version des Regelsatzes und eine Zuordnungskennung fest, und lassen Sie den Wert vollständig aus der Protokollzeile heraus — auch in Fehlerpfaden, denn dort tauchen ausgelaufene Werte am häufigsten auf.

Denken Sie schließlich daran, was die Grenze nicht feststellen kann. Eine bestätigte Form ist eine Aussage über eine Zeichenkette, wie der Fall ohne Prüfziffer für Schemata ohne jede Arithmetik deutlich macht, und die drei Ebenen der Prüfung sind die Referenz dafür, die Ebenen getrennt zu halten.

Nächste Schritte

Nehmen Sie die Schnittstelle, die Ihre sensibelste Kennung entgegennimmt, und listen Sie jede Prüfung auf, die sie ausführt; markieren Sie jede als lokale Arithmetik oder externe Abfrage. Bestätigen Sie dann, dass der Client die lokalen früh ausführt und dass der Server alle erneut ausführt; das Werkzeug zur Nummernprüfung zeigt die Formulierung der Urteile, die ein Client gefahrlos wiederverwenden kann, ohne mehr zu behaupten, als die Arithmetik stützt.

Weiterlesen

Artikel zu Prüfer für Kreditkarten- und SSN-Nummern