500-, 502-, 503- och 504-fel från API:er: vad de betyder och hur de ska hanteras

En statuskod från 500 till 599 innebär att servern inte kunde uppfylla en begäran som såg giltig ut. Din begäran är inte problemet, så det kan fungera att skicka den igen. Om du ska skicka den igen beror på statuskoden och på vad begäran gör. Definitionerna finns i RFC 9110, avsnitt 15.6.

Vad varje statuskod innebär

Statuskod Vad det innebär Försöka igen?
500 Internal Server Error Servern stötte på ett tillstånd som den inte förväntade sig Det beror på API:et. Vissa API:er, till exempel Claude API, säger att du ska försöka igen vid en 500-felkod med exponentiell backoff. Läs API:ets dokumentation.
502 Bad Gateway En gateway eller proxy fick ett ogiltigt svar från servern bakom den Ja, om begäran kan upprepas på ett säkert sätt
503 Service Unavailable Servern är tillfälligt överbelastad eller nere för underhåll, och den bör återställas efter en tid. Servern kan skicka en Retry-After-header. Ja, efter Retry-After om servern skickade ett sådant värde
504 Gateway Timeout En gateway eller proxy fick inget svar i tid från servern bakom den Ja, om begäran är säker att upprepa. Gatewayen slutade vänta, så du vet inte om servern utförde arbetet.

Vilka förfrågningar är säkra att försöka igen

RFC 9110 kallar en metod idempotent när det att skicka samma begäran flera gånger har samma effekt som att skicka den en gång. GET, HEAD, OPTIONS, TRACE, PUToch DELETE är idempotent. POST och PATCH är inte det. Enligt RFC bör en klient inte automatiskt försöka skicka en begäran igen med en icke-idempotent metod om den inte vet att begäran ändå är idempotent, eller att servern aldrig behandlade den ursprungliga begäran. Se Idempotent-metoder.

Ett nytt försök med POST efter 502 eller 504 kan skapa en andra beställning eller skicka ett andra e-postmeddelande. Vissa bibliotek för återförsök försöker med varje metod igen som standard. Till exempel försöker .NET-standardåterhämtningshanteraren igen med POST, såvida du inte anropar options.Retry.DisableForUnsafeHttpMethods(). Mer information finns i Utveckla motståndskraftiga HTTP-appar.

Retry-After vid en 503

En 503 kan innehålla en Retry-After header. Dess värde är antingen ett antal sekunder, till exempel 120, eller ett HTTP-datum, till exempel Fri, 31 Dec 1999 23:59:59 GMT. Koden måste hantera båda. Mer information finns i Retry-After.

Sluta anropa ett API som fortsätter att misslyckas

Återförsök hjälper till med kortvariga fel. När ett API är nere i några minuter lägger att försöka igen för varje begäran till belastning på en server som redan har det svårt, och användarna väntar på att varje nytt försök ska misslyckas. En circuit breaker spårar fel, och när det finns för många slutar den att anropa API:et ett tag och returnerar fel direkt. Efter den tiden släpper det igenom några förfrågningar för att kontrollera om API:et har återställts. Mer information finns i Circuit Breaker. Den .NET standardhanteraren för resiliens innehåller en kretsbrytare som öppnas i 5 sekunder när minst 10 % av begärandena misslyckas i ett 30-sekundersfönster med minst 100 begäranden.

Hantera 5xx-fel

  1. Försök igen vid 502, 503 och 504 endast för idempotenta begäranden. För POST och PATCH, försök bara igen om API:et dokumenterar ett sätt att göra dem säkra att upprepa.
  2. Vänta innan du försöker igen. Använd Retry-After när servern skickar den. Annars använder du exponentiell backoff med slumpmässig jitter och stoppar efter några försök.
  3. Läs API:ets dokument för 500. Försök bara igen om API:et säger att det är säkert.
  4. Sluta anropa ett API som fortsätter att misslyckas. Använd en circuit breaker så att appen misslyckas snabbt medan API:et återställs.
  5. Berätta för användaren vad som hände. Visa "tjänsten har problem, försök igen senare" i stället för ett allmänt fel eller en stackspårning.
const RETRYABLE_STATUS = new Set([502, 503, 504]);
const IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"]);

function retryAfterMs(response) {
  const value = response.headers.get("retry-after");
  if (!value) return null;
  const seconds = Number(value);
  if (Number.isFinite(seconds)) return seconds * 1000;
  const date = Date.parse(value);
  return Number.isNaN(date) ? null : Math.max(0, date - Date.now());
}

export async function fetchWithRetry(url, options = {}, maxRetries = 3) {
  const method = (options.method ?? "GET").toUpperCase();
  const canRetry = IDEMPOTENT_METHODS.has(method);

  for (let attempt = 0; ; attempt++) {
    const response = await fetch(url, options);
    if (!RETRYABLE_STATUS.has(response.status) || !canRetry || attempt === maxRetries) {
      return response;
    }
    await response.body?.cancel();
    const backoff = 2 ** attempt * 1000 + Math.random() * 1000;
    const wait = retryAfterMs(response) ?? backoff;
    await new Promise((resolve) => setTimeout(resolve, wait));
  }
}

Så här testar du att din app hanterar 5xx-fel

Du ser sällan en 5xx när du utvecklar och du kan inte göra så att ett API misslyckas på begäran. Hur du testar avgör om du hittar buggarna innan användarna gör det.

Approach Det här hittar du Vad du saknar
Vänta på produktionsmiljön Faktiska avbrott Allt, tills en användare trycker på den
Mocka API:et i dina tester eller låt kodningsagenten skriva en mock Huruvida felgrenen körs Din verkliga HTTP-klient och bibliotek för återförsök, och hur många gånger det faktiskt gör återförsök. Din app behöver också en testflagga för att nå mocken.
Anropa det verkliga API:et och vänta tills det misslyckas Faktiskt beteende Du kan inte göra så att API:et misslyckas på begäran
Avlyssna appens verkliga trafik och returnera 5xx-fel med en hastighet som du väljer Din faktiska HTTP-klient, ditt återförsöksbibliotek och din avbrottsbrytare Ingenting i din app ändras, så den testar inte din kod isolerat. Spara dina enhetstester till det.

Prova det i din app

Dev Proxy fångar upp appens begäranden och låter en del av dem misslyckas med de fel som du definierar med hjälp av GenericRandomErrorPlugin. Din app anropar den verkliga URL:en hela tiden. Lägg till plugin-programmet i konfigurationsfilen och peka dess errorsFile på en fil med 5xx-felen. I det här exemplet används https://api.contoso.com. Ersätt den med URL:en för API:et som appen anropar.

Fil: server-errors.json

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.errorsfile.schema.json",
  "errors": [
    {
      "request": {
        "url": "https://api.contoso.com/*"
      },
      "responses": [
        { "statusCode": 500 },
        { "statusCode": 502 },
        {
          "statusCode": 503,
          "headers": [
            { "name": "Retry-After", "value": "10" }
          ]
        },
        { "statusCode": 504 }
      ]
    }
  ]
}

Som standard misslyckar insticksprogrammet 50 % av begärandena. Kontrollera i Dev Proxy-utdata att appen skickar GET begäranden på nytt och skickar varje POST endast en gång. Dev Proxy kontrollerar inte om din app väntar på Retry-After vid en 503, så jämför begärandetiderna själv. Starta sedan Dev Proxy med --failure-rate 100 för att se vad din app gör när API:et fortsätter att misslyckas. Mer information finns i Felfrekvens för ändringsbegäran. Information om hur du installerar Dev Proxy finns i Konfigurera Dev Proxy.

Nästa steg

Se även