Begreber
Idempotens
Send en Idempotency-Key med en POST, så et nyt forsøg efter en tabt forbindelse får det første svar i stedet for at tilmelde en person to gange.
Hvorfor
En forbindelse kan blive afbrudt, efter at AttendBeacon har tilmeldt en person, men før din kode har fået det at vide. Sendt igen ville den samme POST udstede en billet mere. Med en Idempotency-Key sker det ikke: den anden forespørgsel får den førstes svar.
Sådan gør du
Lav din egen nøgle til hver forespørgsel (et UUID er ideelt), og send den med din POST. Gem den sammen med forespørgslen, indtil du har fået svar, og send den samme nøgle med ved hvert nyt forsøg.
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))
}
}Hvad der gemmes, og hvor længe
Det første svar på en nøgle (dens status, Location og body) gemmes i 24 timer og gives igen til enhver forespørgsel med samme nøgle, metode, sti og body. Et genafspillet svar har Idempotent-Replayed: true.
| Situation | Svar |
|---|---|
| Et nyt forsøg med en forespørgsel, der er besvaret | Det første svar, med Idempotent-Replayed: true |
| Et nyt forsøg, mens den første stadig behandles | 409 idempotencyKeyInUse, med Retry-After: 1 |
| Samme nøgle med en anden metode, sti eller body | 422 idempotencyKeyReused: brug en ny nøgle til en ny forespørgsel |
| En nøgle på mere end 255 tegn eller med mellemrum | 400 invalidIdempotencyKey |
| Den første forespørgsel fejlede hos AttendBeacon (5xx) | Intet gemmes, så det nye forsøg behandles forfra |
En nøgle hører til den API-nøgle, der sendte den: to integrationer kan ikke støde sammen. De gemte svar kan nævne en person; når den person sletter sine data, slettes de svar, der nævner vedkommende, også.
Hvilke kald
Alle POST-kald tager en Idempotency-Key: tilmelding, udstedelse af billetter, annullering, indtjekning og oprettelse af events, sessioner, rabatkoder og webhooks. PATCH, PUT og DELETE kan i sagens natur gentages sikkert; PATCH tager i stedet If-Match, så en ændring i mellemtiden ikke overskrives (Fejl, preconditionFailed).