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 Beispielscene.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
-
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" -
Senden Sie die Änderung mit
If-Matchund 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.
-
Kommt
409zurü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
200mit der Ressource, undversionist um eins gestiegen (oder unverändert, wenn der Patch nichts geändert hat). - Ein zweiter Patch mit dem alten
If-Matchantwortet409 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. |