Best practices voor het afhandelen van REST API-fouten

1. Inleiding

REST is een staatloze architectuur waarin clients toegang hebben tot bronnen op een server en deze kunnen manipuleren. Over het algemeen gebruiken REST-services HTTP om reclame te maken voor een set bronnen die ze beheren en om een ​​API te bieden waarmee klanten de status van deze bronnen kunnen verkrijgen of wijzigen.

In deze zelfstudie leren we enkele van de best practices voor het omgaan met REST API-fouten, inclusief handige benaderingen om gebruikers relevante informatie te bieden, voorbeelden van grootschalige websites en een concrete implementatie met behulp van een Spring REST-voorbeeldtoepassing.

2. HTTP-statuscodes

Wanneer een client een verzoek indient bij een HTTP-server - en de server ontvangt het verzoek met succes - de server moet de klant informeren of het verzoek met succes is afgehandeld of niet. HTTP doet dit met vijf categorieën statuscodes:

  • 100-niveau (informatief) - Server bevestigt een verzoek
  • 200-niveau (geslaagd) - Server heeft het verzoek voltooid zoals verwacht
  • 300-niveau (omleiding) - Client moet verdere acties uitvoeren om het verzoek te voltooien
  • 400-niveau (Clientfout) - Client heeft een ongeldig verzoek verzonden
  • 500-niveau (serverfout) - Server kon een geldig verzoek niet uitvoeren vanwege een fout met de server

Op basis van de responscode kan een klant het resultaat van een bepaald verzoek inschatten.

3. Fouten afhandelen

De eerste stap bij het afhandelen van fouten is om een ​​cliënt een juiste statuscode te geven. Bovendien moeten we mogelijk meer informatie verstrekken in de reactie-instantie.

3.1. Basisantwoorden

De eenvoudigste manier waarop we met fouten omgaan, is door reageer met een geschikte statuscode.

Enkele veel voorkomende responscodes zijn:

  • 400 Bad Request - Client heeft een ongeldig verzoek verzonden, zoals het ontbreken van de vereiste body of parameter van het verzoek
  • 401 Unauthorized - De client kon zich niet verifiëren bij de server
  • 403 Forbidden - Client geverifieerd maar heeft geen toestemming om toegang te krijgen tot de gevraagde bron
  • 404 niet gevonden - De gevraagde bron bestaat niet
  • 412 Voorwaarde mislukt - Een of meer voorwaarden in de velden van de verzoekheader zijn geëvalueerd als onwaar
  • 500 Internal Server Error - Er is een algemene fout opgetreden op de server
  • 503 Service niet beschikbaar - De gevraagde service is niet beschikbaar

Hoewel ze eenvoudig zijn, stellen deze codes een klant in staat om de brede aard van de opgetreden fout te begrijpen. Als we bijvoorbeeld een 403-foutmelding krijgen, weten we dat we geen toestemming hebben om toegang te krijgen tot de bron die we hebben aangevraagd.

In veel gevallen moeten we echter aanvullende details verstrekken in onze antwoorden.

500-fouten geven aan dat er problemen of uitzonderingen zijn opgetreden op de server tijdens het verwerken van een verzoek. Over het algemeen is deze interne fout niet de zaak van onze klant.

Om dergelijke reacties op de cliënt te minimaliseren, we moeten ijverig proberen interne fouten af ​​te handelen of op te sporen en waar mogelijk reageren met andere geschikte statuscodes. Als er bijvoorbeeld een uitzondering optreedt omdat een aangevraagde bron niet bestaat, moeten we dit weergeven als een 404-fout in plaats van een 500-fout.

Dit wil niet zeggen dat 500 nooit mag worden geretourneerd, alleen dat het moet worden gebruikt voor onverwachte omstandigheden - zoals een serviceonderbreking - waardoor de server het verzoek niet kan uitvoeren.

3.2. Standaard voorjaarsfoutreacties

Deze principes zijn zo alomtegenwoordig dat Spring ze heeft gecodificeerd in zijn standaard foutafhandelingsmechanisme.

Om dit te demonstreren, stel dat we een eenvoudige Spring REST-applicatie hebben die boeken beheert, met een eindpunt om een ​​boek op te halen op basis van zijn ID:

curl -X GET -H "Accept: application / json" // localhost: 8082 / spring-rest / api / book / 1

Als er geen boek is met een ID van 1, verwachten we dat onze controller een BookNotFoundException. Als we een GET uitvoeren op dit eindpunt, zien we dat deze uitzondering is gegenereerd en de antwoordtekst is:

{"timestamp": "2019-09-16T22: 14: 45.624 + 0000", "status": 500, "error": "Internal Server Error", "message": "Geen bericht beschikbaar", "path": " / api / book / 1 "}

Merk op dat deze standaard fout-handler een tijdstempel bevat van wanneer de fout is opgetreden, de HTTP-statuscode, een titel (het fout veld), een bericht (dat standaard leeg is) en het URL-pad waar de fout is opgetreden.

Deze velden bieden een klant of ontwikkelaar informatie om het probleem op te lossen en vormen ook enkele van de velden waaruit de standaardmechanismen voor foutafhandeling bestaan.

Merk bovendien op dat Spring automatisch de HTTP-statuscode 500 retourneert wanneer onze BookNotFoundException wordt gegooid. Hoewel sommige API's een 500-statuscode of andere generieke zullen retourneren, zoals we zullen zien bij de Facebook- en Twitter-API's - voor alle fouten omwille van de eenvoud, het is het beste om indien mogelijk de meest specifieke foutcode te gebruiken.

In ons voorbeeld kunnen we een @ControllerAdvice zodat wanneer een BookNotFoundException wordt gegooid, geeft onze API een status van 404 terug om aan te duiden Niet gevonden in plaats van 500 Interne Server Fout.

3.3. Meer gedetailleerde reacties

Zoals te zien is in het bovenstaande Spring-voorbeeld, is een statuscode soms niet voldoende om de details van de fout weer te geven. Indien nodig kunnen we de hoofdtekst van het antwoord gebruiken om de klant aanvullende informatie te verstrekken. Bij het verstrekken van gedetailleerde antwoorden dienen we het volgende te vermelden:

  • Error - Een unieke identificatie voor de fout
  • Bericht - Een kort voor mensen leesbaar bericht
  • Detail - Een uitgebreidere uitleg van de fout

Als een klant bijvoorbeeld een verzoek verzendt met onjuiste inloggegevens, kunnen we een 401-antwoord sturen met de inhoud van:

{"error": "auth-0001", "message": "Onjuiste gebruikersnaam en wachtwoord", "detail": "Zorg ervoor dat de gebruikersnaam en het wachtwoord in het verzoek correct zijn"}

De fout veld mag niet overeenkomen met de responscode. In plaats daarvan moet het een foutcode zijn die uniek is voor onze applicatie. Over het algemeen is er geen conventie voor de fout veld, verwacht dat het uniek is.

Gewoonlijk bevat dit veld alleen alfanumerieke tekens en verbindende tekens, zoals streepjes of onderstrepingstekens. Bijvoorbeeld, 0001, auth-0001, en onjuiste gebruikerspas zijn canonieke voorbeelden van foutcodes.

De bericht een deel van het lichaam wordt meestal als presentabel beschouwd op gebruikersinterfaces. Daarom moeten we deze titel vertalen als we internationalisering ondersteunen. Dus als een klant een verzoek stuurt met een Accepteer-taal koptekst die overeenkomt met Frans, de titel waarde moet naar het Frans worden vertaald.

De detail- gedeelte is bedoeld voor gebruik door ontwikkelaars van klanten en niet voor de eindgebruiker, dus de vertaling is niet nodig.

Daarnaast kunnen we ook een URL opgeven, zoals de helpen veld - dat klanten kunnen volgen om meer informatie te ontdekken:

{"error": "auth-0001", "message": "Onjuiste gebruikersnaam en wachtwoord", "detail": "Zorg ervoor dat de gebruikersnaam en het wachtwoord in het verzoek correct zijn", "help": "// voorbeeld. com / help / error / auth-0001 "}

Soms, we willen misschien meer dan één fout melden voor een verzoek. In dit geval moeten we de fouten in een lijst retourneren:

{"errors": [{"error": "auth-0001", "message": "Onjuiste gebruikersnaam en wachtwoord", "detail": "Zorg ervoor dat de gebruikersnaam en het wachtwoord in het verzoek correct zijn", "help" : "//voorbeeld.com/help/fout/auth-0001"}, ...]}

En als er een enkele fout optreedt, reageren we met een lijst met één element. Houd er rekening mee dat reageren met meerdere fouten misschien te gecompliceerd is voor eenvoudige toepassingen. In veel gevallen is reageren met de eerste of meest significante fout voldoende.

3.4. Gestandaardiseerde responsorganen

Hoewel de meeste REST-API's vergelijkbare conventies volgen, variëren de specificaties meestal, inclusief de namen van velden en de informatie in de antwoordtekst. Deze verschillen maken het voor bibliotheken en frameworks moeilijk om fouten uniform af te handelen.

In een poging om de REST API-foutafhandeling te standaardiseren, de IETF ontwierp RFC 7807, dat een algemeen foutafhandelingsschema creëert.

Dit schema bestaat uit vijf delen:

  1. type - Een URI-ID die de fout categoriseert
  2. titel - Een kort, voor mensen leesbaar bericht over de fout
  3. toestand - De HTTP-antwoordcode (optioneel)
  4. detail- - Een voor mensen leesbare uitleg van de fout
  5. voorbeeld - Een URI die het specifieke optreden van de fout identificeert

In plaats van onze aangepaste foutresponsbody te gebruiken, kunnen we ons lichaam converteren naar:

{"type": "/ errors / incorrect-user-pass", "title": "Onjuiste gebruikersnaam of wachtwoord.", "status": 401, "detail": "Authenticatie mislukt vanwege onjuiste gebruikersnaam of wachtwoord.", "instance": "/ login / log / abc123"}

Merk op dat de type veld categoriseert het type fout, while voorbeeld identificeert een specifiek optreden van de fout op een vergelijkbare manier als respectievelijk klassen en objecten.

Door URI's te gebruiken, kunnen clients deze paden volgen om meer informatie over de fout te vinden op dezelfde manier dat HATEOAS-links kunnen worden gebruikt om door een REST API te navigeren.

Het volgen van RFC 7807 is optioneel, maar het is voordelig als uniformiteit gewenst is.

4. Voorbeelden

De bovenstaande werkwijzen zijn gebruikelijk in enkele van de meest populaire REST API's. Hoewel de specifieke namen van velden of indelingen per site kunnen verschillen, de algemene patronen zijn bijna universeel.

4.1. Twitter

Laten we bijvoorbeeld een GET-verzoek verzenden zonder de vereiste authenticatiegegevens op te geven:

curl -X GET //api.twitter.com/1.1/statuses/update.json?include_entities=waar

De Twitter API reageert met een fout met de volgende tekst:

{"errors": [{"code": 215, "message": "Slechte verificatiegegevens." }]}

Dit antwoord bevat een lijst met een enkele fout, met zijn foutcode en bericht. In het geval van Twitter is er geen gedetailleerd bericht en wordt een algemene fout - in plaats van een meer specifieke 401-fout - gebruikt om aan te geven dat de authenticatie is mislukt.

Soms is een algemenere statuscode gemakkelijker te implementeren, zoals we zullen zien in ons voorjaarsvoorbeeld hieronder. Het stelt ontwikkelaars in staat groepen uitzonderingen op te vangen en de statuscode die moet worden geretourneerd niet te differentiëren. Indien mogelijk, de meest specifieke statuscode moet worden gebruikt.

4.2. Facebook

Net als bij Twitter, bevat de Graph REST API van Facebook ook gedetailleerde informatie in zijn antwoorden.

Laten we bijvoorbeeld een POST-verzoek uitvoeren om te verifiëren met de Facebook Graph API:

curl -X GET //graph.facebook.com/oauth/access_token?client_id=foo&client_secret=bar&grant_type=baz

We krijgen de volgende foutmelding:

{"error": {"message": "Redirect_uri parameter ontbreekt.", "type": "OAuthException", "code": 191, "fbtrace_id": "AWswcVwbcqfgrSgjG80MtqJ"}}

Net als Twitter gebruikt Facebook ook een generieke fout - in plaats van een meer specifieke fout op 400-niveau - om een ​​fout aan te duiden. Naast een bericht en cijfercode bevat Facebook ook een type veld dat de fout categoriseert en een trace-ID (fbtrace_id) die fungeert als een interne ondersteunings-ID.

5. Conclusie

In dit artikel hebben we enkele van de best practices van REST API-foutafhandeling onderzocht, waaronder:

  • Specifieke statuscodes verstrekken
  • Opnemen van aanvullende informatie in responsinstanties
  • Afhandeling van uitzonderingen op een uniforme manier

Hoewel de details van foutafhandeling per applicatie verschillen, deze algemene principes zijn van toepassing op bijna alle REST API's en moeten waar mogelijk worden nageleefd.

Hierdoor kunnen klanten niet alleen fouten op een consistente manier afhandelen, maar het vereenvoudigt ook de code die we maken bij het implementeren van een REST API.

De code waarnaar in dit artikel wordt verwezen, is beschikbaar op GitHub.