Skip to content

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

StatusBedeutungTypische Ursachen
200ErfolgAnfrage erfolgreich verarbeitet
400Ungültige AnfrageUngültiger Zustandsübergang, fehlerhafte Payload, fehlende Pflichtfelder
401Nicht autorisiertFehlender, ungültiger oder abgelaufener API-Schlüssel
404Nicht gefundenRessource (Bestellung, Befehl) nicht vorhanden
409KonfliktOptimistischer Sperrkonflikt (gleichzeitige Änderung)
429Zu viele AnfragenRatenbegrenzung überschritten (derzeit nicht implementiert, aber einplanen)
500Interner ServerfehlerUnerwarteter Serverfehler
503Dienst nicht verfügbarVorübergehende Server-Nichtverfügbarkeit

Fehlerantwortformate

Die API verwendet zwei verschiedene Fehlerantwortformate.

ProblemDetail (RFC 7807)

Die meisten Fehler vom GlobalExceptionHandler verwenden das standardisierte ProblemDetail-Format:

json
{
  "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:

SzenarioStatusTitelDetail
Ungültiger Zustandsübergang400Illegal argumentNo enum constant ...
Fehlendes Pflichtfeld400Illegal argumentLocation name is required.
Ungültige Bestell-ID (Bestellung nicht vorhanden)400Illegal argumentOrder does not exist
Validierungsfehler400Validation error(Validierungsmeldung)
Datenbank-Constraint-Verletzung400Constraint violationAllgemeine Constraint-Meldung
Lizenzfehler401License error(lizenzbezogene Meldung)
Datenbankoperationsfehler400Something went wrong during database operation.(Fehlerdetails)
Interner Serverfehler500Internal 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
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):

json
{
  "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:

  1. Erkennen Sie den Abbruch über den onerror-Callback (EventSource) oder Verbindungs-Timeout.
  2. Implementieren Sie exponentiellen Backoff: 1s, 2s, 4s, 8s, gedeckelt bei 60s.
  3. Übergeben Sie bei der Wiederverbindung die zuletzt verarbeitete Bestellnummer und Befehls-ID: /notifications/service/stream?orderNumber=12345&commandNumber=42
  4. Wenn der Server neu gestartet wurde, ist Ihre Sitzung möglicherweise verloren. Besorgen Sie einen neuen API-Schlüssel.
javascript
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:

  1. Der SSE onerror wird schnell ausgelöst, wenn Keepalives ausbleiben.
  2. Beginnen Sie als Fallback mit regelmäßigem Polling von GET /orders/location (alle 5 Sekunden).
  3. Versuchen Sie gleichzeitig die SSE-Wiederverbindung.
  4. 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:

javascript
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 Mitnahmebestellungen null sein. Behandeln Sie es als "kein Tisch zugewiesen."
  • userNumber — Kann null sein, wenn die Bestellung ohne Benutzersitzung erstellt wurde.
  • state — Kann bei Befehlsobjekten null sein. Befehle haben immer einen impliziten PLACED-Status.
  • rowsnull bei Befehlsobjekten. Nur Bestellungen haben Zeilen.
  • togo — Kann bei Bestellungen vor Ort null sein.
  • 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 — Kann null sein, wenn keine Rabatte angewendet wurden.
  • customer — Kann null sein, wenn keine Kundeninformationen angegeben wurden.

Sicherer Zugriffsansatz:

javascript
// 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

text
                           +---> 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 PLACED setzen.

Wenn Sie den Status einer Bestellung aktualisieren, zeichnet der Server Zeitstempel auf:

  • orderTime — Wird gesetzt, wenn die Bestellung in PLACED übergeht
  • completeTime — Wird gesetzt, wenn die Bestellung in COMPLETED oder ABORTED übergeht
  • failedTime — Wird gesetzt, wenn die Bestellung in FAILED ü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:

  1. Verwenden Sie die Bestell-number zur Deduplizierung (siehe oben).
  2. Der erste Client, der PUT /orders/{id}/statusupdate aufruft, hat Erfolg. Der zweite Client erhält einen 400-Fehler, weil sich der Status geändert hat.
  3. 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.
  4. 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

bash
curl -i -H "api_key: invalid-key" "https://{host}/orders/location"

Nicht vorhandene Bestellung

bash
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

bash
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