Pojęcia
Idempotentność
Wyślij Idempotency-Key z żądaniem POST, a ponowienie po zerwanym połączeniu otrzyma pierwszą odpowiedź, zamiast rejestrować kogoś dwa razy.
Po co
Połączenie może zostać zerwane, gdy AttendBeacon już kogoś zarejestrował, ale twój kod jeszcze się o tym nie dowiedział. Wysłane ponownie, to samo żądanie POST wydałoby drugi bilet. Z nagłówkiem Idempotency-Key tak się nie stanie: drugie żądanie dostanie odpowiedź na pierwsze.
Jak
Dla każdego żądania utwórz własny klucz (najlepiej UUID) i wyślij go z żądaniem POST. Przechowuj go razem z żądaniem, dopóki nie dostaniesz odpowiedzi, i wysyłaj ten sam klucz przy każdym ponowieniu.
curl -X POST "https://attendbeacon.com/api/v1/events/b3ac0000-0000-4000-8000-000000000001/registrations" \
-H "Authorization: Bearer $ATTENDBEACON_KEY" \
-H "Idempotency-Key: 6f1c2a9e-4b7d-4c0f-8e3a-5b9d1c7e0f42" \
-H "Content-Type: application/json" \
-d '{ "ticketTypeId": "b3ac0006-0000-4000-8000-000000000001", "firstName": "Anna", "lastName": "de Vries", "email": "anna.devries@example.com" }'const idempotencyKey = crypto.randomUUID(); // once per registration, kept for its retries
async function register(person) {
for (let attempt = 1; ; attempt++) {
try {
const response = await fetch(`${base}/events/${eventId}/registrations`, {
method: "POST",
headers: { Authorization: `Bearer ${key}`, "Idempotency-Key": idempotencyKey, "Content-Type": "application/json" },
body: JSON.stringify(person)
});
if (response.status < 500) return response; // an answer: the first, or the first again
} catch { /* the connection dropped: try again with the same key */ }
if (attempt === 4) throw new Error("AttendBeacon did not answer.");
await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 1000));
}
}var idempotencyKey = Guid.NewGuid().ToString(); // once per registration, kept for its retries
for (var attempt = 1; ; attempt++)
{
using var request = new HttpRequestMessage(HttpMethod.Post, $"events/{eventId}/registrations") { Content = JsonContent.Create(person) };
request.Headers.Add("Idempotency-Key", idempotencyKey);
try
{
using var response = await client.SendAsync(request);
if ((int)response.StatusCode < 500) return await response.Content.ReadFromJsonAsync<JsonElement>();
}
catch (HttpRequestException) { /* try again with the same key */ }
if (attempt == 4) throw new InvalidOperationException("AttendBeacon did not answer.");
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
}idempotency_key = str(uuid.uuid4()) # once per registration, kept for its retries
for attempt in range(1, 5):
try:
response = requests.post(
f"{base}/events/{event_id}/registrations",
headers={"Authorization": f"Bearer {key}", "Idempotency-Key": idempotency_key},
json=person,
timeout=30,
)
if response.status_code < 500:
break # an answer: the first, or the first again
except requests.ConnectionError:
pass # try again with the same key
time.sleep(2 ** attempt)$idempotencyKey = [guid]::NewGuid().ToString() # once per registration, kept for its retries
$headers["Idempotency-Key"] = $idempotencyKey
$body = $person | ConvertTo-Json
foreach ($attempt in 1..4) {
try {
$attendee = Invoke-RestMethod -Method Post -Uri "$base/events/$eventId/registrations" -Headers $headers -ContentType "application/json" -Body $body
break
} catch {
if ($_.Exception.Response -and [int]$_.Exception.Response.StatusCode -lt 500) { throw }
Start-Sleep -Seconds ([math]::Pow(2, $attempt))
}
}Co jest przechowywane i jak długo
Pierwsza odpowiedź dla danego klucza (status, Location i treść) jest przechowywana przez 24 godziny i zwracana ponownie każdemu żądaniu z tym samym kluczem, metodą, ścieżką i treścią. Powtórzona odpowiedź zawiera Idempotent-Replayed: true.
| Sytuacja | Odpowiedź |
|---|---|
| Ponowienie żądania, na które już odpowiedziano | Pierwsza odpowiedź z Idempotent-Replayed: true |
| Ponowienie, gdy pierwsze żądanie jest jeszcze przetwarzane | 409 idempotencyKeyInUse z Retry-After: 1 |
| Ten sam klucz z inną metodą, ścieżką lub treścią | 422 idempotencyKeyReused: dla nowego żądania użyj nowego klucza |
| Klucz dłuższy niż 255 znaków lub zawierający spacje | 400 invalidIdempotencyKey |
| Pierwsze żądanie nie powiodło się po stronie AttendBeacon (5xx) | Nic nie jest przechowywane, więc ponowienie jest obsługiwane od nowa |
Klucz należy do klucza API, który go wysłał, więc dwie integracje nie mogą sobie wejść w drogę. Przechowywane odpowiedzi mogą zawierać dane osoby. Gdy ta osoba usunie swoje dane, odpowiedzi, w których występuje, również są usuwane.
Których wywołań to dotyczy
Każde żądanie POST przyjmuje Idempotency-Key: rejestracja, wydawanie biletów, anulowanie, check-in, tworzenie wydarzeń, sesji, kodów rabatowych i webhooków. PATCH, PUT i DELETE można bezpiecznie powtarzać z samej ich natury. PATCH przyjmuje zamiast tego If-Match, aby nie nadpisać zmiany wprowadzonej w międzyczasie (Błędy, preconditionFailed).