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 Zustandsübergang, fehlerhafte Payload, 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 transition",
"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 Zustandsübergang | 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 - Wenn der Server neu gestartet wurde, ist Ihre Sitzung möglicherweise verloren. Besorgen Sie einen neuen API-Schlüssel.
function reconnect(apiKey, lastOrderNumber, lastCommandNumber) {
const url = `https://{host}/notifications/service/stream` +
`?api_key=${apiKey}` +
`&orderNumber=${lastOrderNumber}` +
`&commandNumber=${lastCommandNumber}`;
// ... neuen EventSource öffnen
}Netzwerkpartitionierung
Wenn das Netzwerk partitioniert wird (der SSE-Stream bricht ab, aber der Client kann den Server nicht erreichen), wird die SSE-Verbindung nach 60 Minuten Inaktivität ein Timeout auslösen. Die Keepalive-Ereignisse (alle 15 Sekunden) werden jedoch viel früher fehlschlagen.
Wiederherstellungsstrategie:
- Der SSE
onerrorwird schnell ausgelöst, wenn Keepalives ausbleiben. - Beginnen Sie als Fallback mit regelmäßigem Polling von
GET /orders/location(alle 5 Sekunden). - Versuchen Sie gleichzeitig die SSE-Wiederverbindung.
- Sobald die SSE-Verbindung wiederhergestellt ist, setzen Sie das SSE-gesteuerte Polling fort und reduzieren Sie das Fallback-Intervall.
Duplikaterkennung
Das Feld number in jedem ExternalOrder ist eine eindeutige, monoton steigende Kennung. Verwenden Sie es für die Idempotenz:
const processedOrders = new Set();
function processOrder(order) {
if (processedOrders.has(order.number)) {
return; // Bereits verarbeitet
}
processedOrders.add(order.number);
// ... Bestellung verarbeiten
}Behalten Sie den Satz verarbeiteter IDs mindestens so lange, bis die Bestellung abgeschlossen oder storniert ist. Sie können alte Einträge nach einem sicheren Zeitraum (z. B. 1 Stunde) löschen.
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.isCommand— Immer vorhanden.truebei Befehlen,falsebei Bestellungen.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. Ein serverseitiger Wiederholungsjob kann fehlgeschlagene Bestellungen zurück auf
PLACEDsetzen.
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 — Nur ein Client sollte jede Bestellung verarbeiten
Wenn mehrere Service-Clients denselben Standort abfragen, erhalten sie beide dieselben Bestellungen. Um doppelte Verarbeitung zu vermeiden:
- Verwenden Sie die Bestell-
numberzur Deduplizierung (siehe oben). - Der erste Client, der
PUT /orders/{id}/statusupdateaufruft, hat Erfolg. Der zweite Client erhält einen 400-Fehler, weil sich der Status geändert hat. - Wenn der zweite Client einen 400-Fehler erhält, sollte er den aktuellen Bestellstatus über Polling überprüfen und die Bestellung überspringen, wenn sie bereits verarbeitet wurde.
- Bei optimistischen Sperrkonflikten (409) wiederholt der Server intern. Wenn der Konflikt bestehen bleibt, aktualisieren Sie Ihre Ansicht der Bestellung.
Umgang mit Timeouts
- SSE-Stream: 60-minütiges Inaktivitäts-Timeout. Keepalives verhindern dies im Normalbetrieb.
- HTTP-Anfragen: Setzen Sie ein angemessenes Timeout von 30 Sekunden für reguläre API-Aufrufe.
- SSE-Verbindung: Setzen Sie ein Timeout von mindestens 65 Sekunden, um Keepalive-Intervalle zu berücksichtigen.
- Netzwerkfehler: Wiederholen Sie mit exponentiellem Backoff. Wiederholen Sie nicht unbegrenzt.
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