Fehlerbehandlung & Randfälle
Eine robuste Fehlerbehandlung ist für eine zuverlässige Integration unerlässlich. Dieses Kapitel behandelt die HTTP-Statuscodes, auf die Sie stoßen werden, die Fehlerantwortformate und Hinweise zur Behandlung von Randfällen.
HTTP-Statuscode-Referenz
| Status | Bedeutung | Typische Ursachen |
|---|---|---|
| 200 | Erfolg | Anfrage erfolgreich verarbeitet |
| 400 | Ungültige Anfrage | Ungültiger Zustandsname, fehlerhafte Nutzlast, fehlende Pflichtfelder |
| 401 | Nicht autorisiert | Fehlender, ungültiger oder abgelaufener API-Schlüssel |
| 404 | Nicht gefunden | Ressource (Bestellung, Befehl) nicht vorhanden |
| 409 | Konflikt | Optimistischer Sperrkonflikt (gleichzeitige Änderung) |
| 429 | Zu viele Anfragen | Ratenbegrenzung überschritten (derzeit nicht implementiert, aber einplanen) |
| 500 | Interner Serverfehler | Unerwarteter Serverfehler |
| 503 | Dienst nicht verfügbar | Vorübergehende Server-Nichtverfügbarkeit |
Fehlerantwortformate
Die API verwendet zwei verschiedene Fehlerantwortformate.
ProblemDetail (RFC 7807)
Die meisten Fehler vom GlobalExceptionHandler verwenden das standardisierte ProblemDetail-Format:
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Invalid order state name",
"instance": "/orders/12345/statusupdate"
}Das Feld type ist typischerweise "about:blank". Der title ist eine kurze Klassifizierung. Der detail enthält eine menschenlesbare Erklärung. Der instance ist der Request-URI, der den Fehler verursacht hat.
Häufige ProblemDetail-Antworten:
| Szenario | Status | Titel | Detail |
|---|---|---|---|
| Ungültiger Zustandsname | 400 | Illegal argument | No enum constant ... |
| Fehlendes Pflichtfeld | 400 | Illegal argument | Location name is required. |
| Ungültige Bestell-ID (Bestellung nicht vorhanden) | 400 | Illegal argument | Order does not exist |
| Validierungsfehler | 400 | Validation error | (Validierungsmeldung) |
| Datenbank-Constraint-Verletzung | 400 | Constraint violation | Allgemeine Constraint-Meldung |
| Lizenzfehler | 401 | License error | (lizenzbezogene Meldung) |
| Datenbankoperationsfehler | 400 | Something went wrong during database operation. | (Fehlerdetails) |
| Interner Serverfehler | 500 | Internal server error | {uuid}: {message} |
Jede 500-Antwort enthält eine UUID für die Rückverfolgung. Protokollieren Sie diese UUID bei der Fehlerbehebung.
Legacy-401-Antworten
Authentifizierungsfehler geben zwei mögliche Formate zurück:
Vom AuthorizationInterceptor (fehlender/ungültiger Schlüssel):
HTTP/1.1 401 Unauthorized
Content-Type: text/plain
{"error" : "Session not valid."}Hinweis: Diese Antwort hat Content-Type: text/plain, nicht application/json, auch wenn der Body JSON ist. Die Antwort enthält eine zufällige Verzögerung von 500-1499 ms.
Vom UnAuthorizedException-Handler (abgelaufene Sitzung):
{
"message": "Unauthorized",
"uid": null
}Beide bedeuten dasselbe: Der API-Schlüssel ist nicht gültig. Authentifizieren Sie sich neu oder besorgen Sie einen neuen Schlüssel.
Randfälle
SSE-Wiederverbindung mit Offset-Parametern
Wenn die SSE-Verbindung abbricht:
- Erkennen Sie den Abbruch über den
onerror-Callback (EventSource) oder Verbindungs-Timeout. - Implementieren Sie exponentiellen Backoff: 1s, 2s, 4s, 8s, gedeckelt bei 60s.
- Übergeben Sie bei der Wiederverbindung die zuletzt verarbeitete Bestellnummer und Befehls-ID:
/notifications/service/stream?orderNumber=12345&commandNumber=42 - Fragen Sie nach der Wiederverbindung offene Vorgänge ab. Prüfen Sie einen vorhandenen Schlüssel vor dem Ersatz; das Schließen des Streams belegt keinen ungültigen Schlüssel.
Verwenden Sie die Anleitung zum Verbindungsaufbau und erneuten Verbinden.
Netzwerkpartitionierung
Warten Sie zur Erkennung einer unterbrochenen Verbindung nicht auf das konfigurierte Emitter-Zeitlimit des Servers. Überwachen Sie eingehende Ereignisse mit einer lokalen Lese-/Leerlaufschwelle. Fehlende Keepalives garantieren keinen sofortigen Transportfehler.
Verwenden Sie bei erreichbarem Server begrenztes Fallback-Polling und verbinden Sie SSE mit Backoff erneut. Bei unerreichbarem Server begrenzen Sie beide Vorgänge. Fragen Sie nach der Wiederherstellung offene Vorgänge ab und gleichen Sie dauerhafte Verarbeitungsstände ab, bevor Sie erneut an die Kasse senden.
Duplikaterkennung
Verwenden Sie getrennte dauerhafte Verarbeitungsstände für Bestellungen und Befehle, zugeordnet zu Installation und Standort. Ihre number-Felder gehören zu unterschiedlichen Kennungsfolgen. Ein Satz im Arbeitsspeicher schützt nach einem Neustart nicht vor Duplikaten.
Koordinieren Sie Worker vor der externen Übergabe. Speichern Sie externes Ergebnis und Bestätigung getrennt. Klären Sie unbekannte Ergebnisse mit der Kasse vor einem erneuten Versuch. Richten Sie die Aufbewahrung nach dem Wiederherstellungsbedarf aus; ein pauschaler Ablauf nach einer Stunde verhindert Duplikate nicht zuverlässig.
Teilweise Daten — Nullable-Felder
Felder ohne Wert werden aus der Antwort weggelassen — die API serialisiert null nicht. Ihr Client darf nicht davon ausgehen, dass ein Feld immer vorhanden ist:
tableNumber— Kann bei Mitnahmebestellungen fehlen. Behandeln Sie es als "kein Tisch zugewiesen."userNumber— Kann fehlen, wenn die Bestellung ohne Benutzersitzung erstellt wurde.state— Immer vorhanden. Befehle melden immerPLACED.rows— Nur bei Bestellungen vorhanden. Befehle haben keine Zeilen.guestCount— Bei Bestellungen vorhanden (aktuell immer1).readTableState— Nur bei Tischstatus-lesen-Befehlen vorhanden (true).paymentNumber— Nur bei Rechnung-anfordern-Befehlen vorhanden (immer1).exportMainData— Nur bei Daten-exportieren-/Remote-Menü-abrufen-Befehlen vorhanden (true).
Das DTO definiert außerdem togo, moveTableTo, receipt, paymentDiscount und customer, aber das aktuelle Backend füllt sie nie aus, daher erscheinen sie nie in Antworten.
Sicherer Zugriffsansatz:
// Optionale Verkettung und Nullish Coalescing verwenden
const table = order.tableNumber ?? "unbekannt";
const state = order.state ?? "PLACED";
const rows = order.rows ?? [];
const paymentDiscounts = order.paymentDiscount ?? [];Bestellstatus-Maschine
+---> COMPLETED
|
SELECTING --> NEW --> PLACED ----> ABORTED
|
+---> FAILED ---> PLACED (Wiederholung)- SELECTING: Kunde fügt Artikel hinzu. Für Service-Clients über den Standort-Endpunkt nicht sichtbar.
- NEW: Bestellung von einer Client-App erstellt. Noch nicht aufgegeben.
- PLACED: Kunde hat die Bestellung aufgegeben. Dies ist der Status, den Ihr Service-Client beim Polling sieht.
- COMPLETED: Bestellung erfüllt. Von Ihrem Service-Client über die Statusaktualisierung gesetzt.
- ABORTED: Bestellung storniert. Von Ihrem Client gesetzt oder wenn eine Sitzung geschlossen wird.
- FAILED: Verarbeitungsfehler. Unterhalb der konfigurierten Fehlergrenze wird die Bestellung bei einer Fehlermeldung wieder auf
PLACEDgesetzt; an der Grenze ist eine Wiederherstellung durch den Betreiber nötig. Siehe Fehlerbehandlung.
Wenn Sie den Status einer Bestellung aktualisieren, zeichnet der Server Zeitstempel auf:
orderTime— Wird gesetzt, wenn die Bestellung inPLACEDübergehtcompleteTime— Wird gesetzt, wenn die Bestellung inCOMPLETEDoderABORTEDübergehtfailedTime— Wird gesetzt, wenn die Bestellung inFAILEDübergeht
Gleichzeitigkeit — Integrationsworker koordinieren
Mehrere Polling-Clients können dieselben Bestellungen erhalten. Koordinieren Sie Worker und speichern Sie Verarbeitungskennungen vor der externen Übergabe. Eine zweite Statusänderung muss nicht wegen der ersten fehlschlagen. Der Endpunkt reserviert keine Arbeit; setzen Sie keine transparente Konfliktwiederholung voraus. Klären Sie unbekannte Ergebnisse vor erneuter externer Verarbeitung.
Umgang mit Timeouts
Unterscheiden Sie Verbindungsaufbau, Zeitgrenzen normaler Anfragen und SSE-Lese-/Leerlaufüberwachung. Beispielsweise können Sie normale Anfragen auf 30 Sekunden begrenzen und 60 Sekunden ohne Streamereignis untersuchen. Dies sind Vorschläge für die Clientkonfiguration, keine garantierten Serverantwortzeiten.
Klären Sie nach einem Schreib-Timeout das Ergebnis vor einer Wiederholung. Siehe Sicherheit und bewährte Verfahren.
Curl: Fehlerszenarien simulieren
Ungültiger API-Schlüssel
curl -i -H "api_key: invalid-key" "https://{host}/orders/location"Nicht vorhandene Bestellung
curl -i -X PUT \
-H "api_key: abc123-def456-ghi789" \
-H "Content-Type: application/json" \
-d '{"newState": "COMPLETED"}' \
"https://{host}/orders/99999/statusupdate"Ungültige JSON-Payload
curl -i -X PUT \
-H "api_key: abc123-def456-ghi789" \
-H "Content-Type: application/json" \
-d 'not-json' \
"https://{host}/orders/12345/statusupdate"Nächste Schritte
- Sicherheit & Best Practices — Ihre Integration absichern und Best Practices befolgen