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
Mehrere Felder in ExternalOrder können null sein. Ihr Client muss diese ordnungsgemäß behandeln:
tableNumber— Kann bei Mitnahmebestellungennullsein. Behandeln Sie es als "kein Tisch zugewiesen."userNumber— Kannnullsein, wenn die Bestellung ohne Benutzersitzung erstellt wurde.state— Kann bei Befehlsobjektennullsein. Befehle haben immer einen implizitenPLACED-Status.rows—nullbei Befehlsobjekten. Nur Bestellungen haben Zeilen.togo— Kann bei Bestellungen vor Ortnullsein.moveTableTo— Nur bei Tisch-verschieben-Befehlen nicht null.readTableState— Nur bei Tischstatus-lesen-Befehlen nicht null.paymentNumber— Nur bei Rechnung-anfordern-Befehlen nicht null.paymentDiscount— Kannnullsein, wenn keine Rabatte angewendet wurden.customer— Kannnullsein, wenn keine Kundeninformationen angegeben wurden.
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