Zum Inhalt springen

Gleichzeitige Änderungen: ETag und If-Match

Eine versionierte Ressource lesen, die Version als If-Match zurückschicken und einen 409-Konflikt sauber auflösen.

4 Minuten LesezeitAPIStand: 25.10.2026

Auf dieser Seite

Bevor Sie loslegen

Für wen
Wer Szenen, Playlisten, Programme, Rollen oder Displays über die API ändert.
Voraussetzung
ein Schlüssel ak_… mit einem schreibenden Recht — im Beispiel scene.update (Klasse Inhalte-Pflege)
Ort
Kommandozeile

Nach dieser Anleitung ändern Sie eine versionierte Ressource so, dass eine gleichzeitige Änderung eines anderen Clients nicht verloren geht: Sie lesen das Kennzeichen aus der Kopfzeile ETag, schicken es als If-Match zurück und lösen einen 409-Konflikt sauber auf.

Wie die Versionsprüfung funktioniert

Fünf Ressourcen führen ein Feld version: Szenen, Playlisten, Programme (Zeitpläne), Rollen und Displays. Bei einer Änderung senden Sie den gelesenen Wert als Kopfzeile If-Match. Hat inzwischen jemand anderes geschrieben, antwortet die API mit 409 Conflict.OptimisticLock, und Sie lesen neu.

Die Version ist der ETag. Sie steht zweimal in der Antwort: als Feld version im Rumpf und als Kopfzeile ETag: "7" — ein starkes Kennzeichen, kein W/. Beide Auftritte tragen denselben Wert; welchen Sie lesen, ist Ihre Wahl. Die Kopfzeile ist auch für Browser-Clients auf anderen Ursprüngen freigegeben.

ETag senden die Lese- und die Änderungsroute derselben Ressourcen: GET und PATCH auf /v1/scenes/{id}, /v1/playlists/{id}, /v1/schedules/{id}, /v1/roles/{id} und /v1/players/{id} sowie POST /v1/scenes/{id}/versions/{vid}/revert. Trägt eine Antwort ausnahmsweise keine Fassung, bleibt die Kopfzeile weg, statt eine Null zu behaupten.

Schritte

  1. Lesen Sie die Ressource und merken Sie sich die Fassung — aus der Kopfzeile oder aus dem Rumpf:

    curl -sS -D - -o /dev/null "$SZENERA_API/v1/scenes/scn_01KJPX8NH0SZENE00000000001" \
      -H "Authorization: Bearer $SZENERA_API_KEY" | grep -i '^etag:'
    
    ETag: "7"
    
  2. Senden Sie die Änderung mit If-Match und genau diesem Wert:

    curl -sS -X PATCH "$SZENERA_API/v1/scenes/scn_01KJPX8NH0SZENE00000000001" \
      -H "Authorization: Bearer $SZENERA_API_KEY" \
      -H "Content-Type: application/json" \
      -H 'If-Match: "7"' \
      -d '{ "description": "Startbild im Schaufenster: Wortmarke, Wochenplan und Uhrzeit." }'
    

    Die Antwort ist die Szene mit der neuen Version (hier auf vier Felder gekürzt):

    { "id": "scn_01KJPX8NH0SZENE00000000001", "name": "Notdienst & Wetter", "version": 8, "updatedAt": "2026-07-06T08:30:00.000Z" }
    

    Ein inhaltsgleicher Patch lässt die Version stehen.

  3. Kommt 409 zurück, hat jemand zwischen Schritt 1 und 2 geschrieben:

    {
      "code": "Conflict.OptimisticLock",
      "message": "Scene draft was modified concurrently",
      "traceId": "trc_…",
      "details": { "sceneId": "scn_01KJPX8NH0SZENE00000000001", "expectedVersion": 7 }
    }
    

    Lesen Sie die Ressource neu (Schritt 1), wenden Sie Ihre Änderung auf den frischen Stand an und senden Sie erneut mit der neuen Version. Wiederholen Sie den Patch nicht blind mit der alten Version — das endet wieder in 409.

Optionen

If-Match nimmt drei Schreibweisen desselben Werts an:

Kopfzeile Wirkung
If-Match: "7" die Regelform: das Kennzeichen, wie die Antwort es gesendet hat
If-Match: W/"7" die schwache Form, ebenfalls angenommen
If-Match: 7 die nackte Zahl aus dem Rumpf — der Bestand, damit kein laufender Client bricht
If-Match: 0 oder kein Zahlenwert 422 Validation.Failed („If-Match must be a positive resource version“)
kein If-Match auf Routen, die die Kopfzeile erzwingen: 422 Validation.Failed („If-Match header required for this operation“)
Displays fehlende Kopfzeile endet in 409 Conflict.OptimisticLock (Versionsvergleich gegen 0), nie in 422

Fünf Operationen erzwingen die Kopfzeile und führen sie in der Referenz als Pflicht-Parameter: PATCH /v1/scenes/{id}, POST /v1/scenes/{id}/versions/{vid}/revert, PATCH /v1/playlists/{id}, PATCH /v1/schedules/{id} und PATCH /v1/roles/{id}. PATCH /v1/players/{id} nimmt sie an, verlangt sie aber nicht: Ohne Kopfzeile prüft die Route gegen Fassung 0 und läuft damit in denselben Konflikt wie eine veraltete Angabe. Bei Programmen liegt version unter schedule.version, bei Rollen unter role.version, bei Displays unter player.version.

Was details bei einem Konflikt trägt, hängt von der Ressource ab: Szenen nennen sceneId und expectedVersion; Rollen resource, resourceId, expectedVersion und actualVersion; Displays playerId, expectedVersion und actualVersion; Playlisten und Programme entity, id und expectedVersion.

Kommandozeile. szenera scene update <id> --version=7 … sendet die Version als If-Match; ohne --version liest der Befehl sie vorher selbst. --retry liest bei einem Konflikt einmal neu und wiederholt. Die Spalte Fassung in szenera scene list ist derselbe Wert. Details unter Befehle für Ihre Organisation.

Ergebnis prüfen

  • Die Antwort auf den Patch ist 200 mit der Ressource, und version ist um eins gestiegen (oder unverändert, wenn der Patch nichts geändert hat).
  • Ein zweiter Patch mit dem alten If-Match antwortet 409 Conflict.OptimisticLock.

Wenn etwas nicht klappt

Symptom Bedeutung Lösung
409 Conflict.OptimisticLock Ein anderer Client hat zwischen Lesen und Schreiben geändert. Neu lesen, Änderung erneut anwenden, mit neuer Version senden.
422 Validation.Failed mit „If-Match header required for this operation“ Eine der fünf Routen oben erzwingt die Kopfzeile. If-Match mit dem gelesenen Kennzeichen mitsenden.
422 Validation.Failed mit „If-Match must be a positive resource version“ Der Wert ist keine Zahl ab 1 — etwa ein selbst gebildetes Kennzeichen aus mehreren Teilen. Das Kennzeichen aus der Antwort senden, "7", W/"7" oder 7.
409 Conflict.OptimisticLock bei einem Display, obwohl niemand geschrieben hat Die Kopfzeile fehlte; die Route vergleicht dann gegen Fassung 0. If-Match mit dem gelesenen Kennzeichen mitsenden.
Sie erwarten 412 Es gibt keinen 412 — der Konflikt ist immer 409. Auf 409 und den Code Conflict.OptimisticLock schalten.
Kein ETag-Kopf in der Antwort Die Route ist nicht versioniert, oder die Antwort trug ausnahmsweise keine Fassung. version aus dem Rumpf lesen; fehlt auch die, kennt die Ressource keine Fassung.

Weiterführendes