API REST back-end : statuts HTTP et sémantique des réponses

Capítulo 3

Temps de lecture estimé : 9 minutes

+ Exercice

Pourquoi la sémantique HTTP compte

Une API REST « propre » ne se contente pas de renvoyer des données : elle communique un état via le code HTTP, des en-têtes et (parfois) un corps de réponse. Une sémantique prévisible permet au client de prendre des décisions simples : réessayer ou non, afficher un message, invalider un cache, corriger une requête, etc. L’objectif est d’être cohérent : mêmes scénarios → mêmes codes, mêmes formats d’erreur, mêmes en-têtes.

Succès : choisir entre 200, 201 et 204

200 OK : succès avec représentation

Utilisez 200 lorsque la réponse contient une représentation utile : lecture d’une ressource, liste, ou résultat d’une opération qui renvoie un corps.

  • GET d’une ressource existante → 200 + JSON de la ressource.
  • PATCH/PUT réussi et vous renvoyez la ressource mise à jour → 200 + JSON.
  • POST qui exécute une action et renvoie un résultat (sans créer une nouvelle ressource) → 200 + JSON du résultat.
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "v7"

{ "id": "u_123", "email": "a@exemple.com", "status": "active" }

201 Created : création d’une nouvelle ressource

Utilisez 201 quand une requête crée effectivement une nouvelle ressource. La bonne pratique est d’inclure l’en-tête Location pointant vers l’URL canonique de la ressource créée. Vous pouvez aussi renvoyer un corps (souvent la représentation de la ressource créée) pour éviter un GET supplémentaire.

Quand renvoyer 201 avec Location (étapes pratiques)

  • 1) Le serveur génère l’identifiant (ou accepte un identifiant client si votre design le permet).
  • 2) Il persiste la ressource.
  • 3) Il construit l’URL canonique (stable) de la ressource.
  • 4) Il renvoie 201 + Location + (optionnel) corps JSON.
HTTP/1.1 201 Created
Location: /users/u_123
Content-Type: application/json

{ "id": "u_123", "email": "a@exemple.com", "status": "pending" }

À éviter : renvoyer 200 pour une création si vous pouvez renvoyer 201. Le code 201 simplifie la logique client (création confirmée + emplacement connu).

Continuez dans notre application.
  • Écoutez le fichier audio avec l'écran éteint.
  • Obtenez un certificat à la fin du programme.
  • Plus de 5000 cours à découvrir !
Ou poursuivez votre lecture ci-dessous...
Download App

Téléchargez l'application

204 No Content : succès sans corps

Utilisez 204 lorsque l’opération réussit mais qu’il n’y a rien d’utile à renvoyer. Un 204 ne doit pas contenir de corps. C’est typique pour une suppression, ou une mise à jour où le client n’a pas besoin de représentation.

Quand utiliser 204 (étapes pratiques)

  • 1) Exécuter l’opération (ex. suppression).
  • 2) Ne pas sérialiser de JSON.
  • 3) Renvoyer 204 (éventuellement avec des en-têtes utiles, ex. invalidation cache côté client).
HTTP/1.1 204 No Content
Cache-Control: no-store

Cas fréquent : DELETE /users/u_123204 si la suppression a eu lieu. Si la ressource n’existe pas, préférez 404 (voir plus bas) pour rester explicite.

Erreurs côté client : 400, 401, 403, 404, 409, 422

Les codes 4xx indiquent que le client doit modifier sa requête (ou son contexte d’authentification/autorisation). Pour garder une sémantique prévisible, distinguez : format invalide (400), authentification (401), autorisation (403), absence (404), conflit (409), validation métier/champs (422).

400 Bad Request : requête mal formée

Utilisez 400 quand la requête est syntaxiquement incorrecte ou impossible à interpréter : JSON invalide, type inattendu, paramètre manquant au niveau protocolaire, pagination incohérente, etc.

HTTP/1.1 400 Bad Request
Content-Type: application/json

{ "error": { "code": "bad_request", "message": "Corps JSON invalide." } }

401 Unauthorized : authentification requise ou invalide

Utilisez 401 si l’utilisateur n’est pas authentifié ou si le jeton/credential est invalide/expiré. En HTTP, 401 implique souvent un en-tête WWW-Authenticate (selon le schéma utilisé).

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
Content-Type: application/json

{ "error": { "code": "unauthorized", "message": "Authentification requise." } }

403 Forbidden : authentifié mais non autorisé

Utilisez 403 quand l’utilisateur est authentifié mais n’a pas les droits nécessaires. Évitez de détailler la règle exacte côté réponse si cela révèle des informations sensibles (rôles, existence de ressources, politiques internes).

HTTP/1.1 403 Forbidden
Content-Type: application/json

{ "error": { "code": "forbidden", "message": "Accès refusé." } }

404 Not Found : ressource inexistante (ou non révélée)

Utilisez 404 quand la ressource demandée n’existe pas. Dans certains contextes, vous pouvez aussi renvoyer 404 au lieu de 403 pour éviter de révéler l’existence d’une ressource (stratégie de non-divulgation). L’important est d’être cohérent sur une même famille d’endpoints.

Structurer une 404 utile (sans fuite d’informations)

  • Inclure un code d’erreur stable (machine-readable).
  • Inclure un message générique (humain) sans confirmer des détails sensibles.
  • Optionnel : inclure l’identifiant demandé si ce n’est pas sensible (souvent acceptable car fourni par le client).
  • Inclure un identifiant de corrélation (request id) via en-tête ou champ pour support.
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-Id: 8f3c2a

{ "error": { "code": "not_found", "message": "Ressource introuvable.", "details": { "resource": "user", "id": "u_999" } } }

409 Conflict : conflit d’état ou d’unicité

Utilisez 409 quand la requête est valide mais ne peut pas être appliquée à cause d’un conflit avec l’état actuel : contrainte d’unicité (email déjà utilisé), version concurrente, transition d’état impossible (ex. tenter d’activer un compte déjà supprimé), ou conflit d’ETag (optimistic locking).

Structurer une 409 utile

  • Indiquer la nature du conflit via un code stable (ex. conflict, duplicate, etag_mismatch).
  • Donner un message actionnable (ex. « choisir un autre email », « recharger puis réessayer »).
  • Ne pas divulguer d’informations sur des ressources appartenant à d’autres utilisateurs (ex. « cet email appartient à X » est à proscrire).
HTTP/1.1 409 Conflict
Content-Type: application/json

{ "error": { "code": "duplicate", "message": "Valeur déjà utilisée.", "details": { "field": "email" } } }

422 Unprocessable Entity : validation métier/champs

Utilisez 422 lorsque la requête est bien formée (syntaxe OK) mais échoue la validation : champs hors bornes, format d’email valide mais non accepté, règle métier (date de fin avant date de début), etc. Cela aide le client à distinguer une erreur de parsing (400) d’une erreur de validation (422).

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{ "error": { "code": "validation_failed", "message": "Certains champs sont invalides.", "details": { "fields": [ { "name": "password", "reason": "too_short" } ] } } }

Incidents serveur : 500 et 503

500 Internal Server Error : erreur inattendue

Utilisez 500 pour une erreur non prévue côté serveur. Le corps doit rester générique : ne renvoyez pas de stack trace, de requêtes SQL, ni de détails d’infrastructure. En revanche, fournissez un identifiant de corrélation pour permettre au support de retrouver les logs.

HTTP/1.1 500 Internal Server Error
Content-Type: application/json
X-Request-Id: 8f3c2a

{ "error": { "code": "internal_error", "message": "Une erreur est survenue." } }

503 Service Unavailable : indisponibilité temporaire

Utilisez 503 quand le service est temporairement indisponible (maintenance, surcharge, dépendance critique indisponible). Si vous savez quand le client peut réessayer, utilisez Retry-After.

HTTP/1.1 503 Service Unavailable
Retry-After: 30
Content-Type: application/json

{ "error": { "code": "service_unavailable", "message": "Service temporairement indisponible, réessayez plus tard." } }

Format d’erreur cohérent : messages actionnables sans divulgation

Principes de base

  • Stabilité : un champ error.code stable (machine-readable) est plus fiable qu’un texte.
  • Lisibilité : un message court, orienté action.
  • Détails contrôlés : un objet details optionnel pour les champs invalides, sans données sensibles.
  • Corrélation : un X-Request-Id (ou équivalent) pour l’investigation.
  • Pas de fuite : ne révélez pas l’existence de comptes, d’emails, de ressources d’autres utilisateurs, ni la cause interne exacte (ex. « violation index unique users_email_idx »).

Gabarit recommandé

{
  "error": {
    "code": "...",
    "message": "...",
    "details": { }
  }
}

Stratégie côté client (ce que l’API doit permettre)

Pour aider le client à afficher des messages appropriés sans dépendre de textes, alignez vos réponses sur une stratégie simple :

  • 4xx : le client peut corriger (afficher un message, surligner un champ, demander une reconnexion).
  • 409 : le client doit résoudre un conflit (changer une valeur, recharger l’état, gérer concurrence).
  • 5xx : le client peut proposer de réessayer plus tard (avec backoff), et afficher un message générique.

En-têtes utiles : ETag et Cache-Control (quand pertinent)

ETag : cache et concurrence optimiste

ETag est un identifiant de version d’une représentation. Il sert à (1) éviter des transferts inutiles et (2) protéger des mises à jour concurrentes.

Étapes pratiques : GET conditionnel avec ETag

  • 1) Le serveur renvoie ETag sur GET.
  • 2) Le client renvoie If-None-Match sur un GET ultérieur.
  • 3) Si inchangé, le serveur renvoie 304 Not Modified sans corps.
GET /users/u_123
If-None-Match: "v7"

HTTP/1.1 304 Not Modified
ETag: "v7"

Étapes pratiques : mise à jour protégée

  • 1) Le client lit la ressource et récupère ETag.
  • 2) Le client envoie If-Match lors du PUT/PATCH.
  • 3) Si l’ETag ne correspond plus, renvoyer 409 (ou 412 Precondition Failed si vous adoptez cette variante) avec un message « recharger puis réessayer ».
PATCH /users/u_123
If-Match: "v7"
Content-Type: application/json

{ "status": "active" }

HTTP/1.1 200 OK
ETag: "v8"
Content-Type: application/json

{ "id": "u_123", "status": "active" }

Cache-Control : contrôler la mise en cache

Cache-Control indique si et comment une réponse peut être mise en cache. Utilisez-le lorsque c’est pertinent (ressources publiques, données rarement modifiées, ou au contraire données sensibles).

  • Données sensibles / personnalisées : Cache-Control: no-store (évite stockage).
  • Données cacheables : Cache-Control: public, max-age=60 (ou private si spécifique à un utilisateur).
  • Validation : combinez avec ETag pour des caches efficaces.
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: private, max-age=60
ETag: "v7"

{ "id": "u_123", "status": "active" }

Checklist de sémantique prévisible (à appliquer endpoint par endpoint)

ScénarioCodeCorpsEn-têtes conseillés
Lecture OK200OuiETag, Cache-Control (si pertinent)
Création OK201OptionnelLocation, (ETag si vous renvoyez la représentation)
Suppression OK204NonCache-Control (selon sensibilité)
JSON invalide / paramètres incohérents400Oui (erreur)X-Request-Id
Non authentifié401Oui (erreur)WWW-Authenticate
Non autorisé403Oui (erreur)X-Request-Id
Ressource absente (ou masquée)404Oui (erreur)X-Request-Id
Conflit (unicité / concurrence)409Oui (erreur)ETag (si lié à version), X-Request-Id
Validation métier/champs422Oui (erreur)X-Request-Id
Erreur interne500Oui (générique)X-Request-Id
Indisponibilité temporaire503Oui (générique)Retry-After, X-Request-Id

Répondez maintenant à l’exercice sur le contenu :

Lorsqu’une requête crée réellement une nouvelle ressource, quelle réponse est la plus cohérente pour aider le client à confirmer la création et connaître l’URL canonique de cette ressource ?

Tu as raison! Félicitations, passez maintenant à la page suivante

Vous avez raté! Essayer à nouveau.

Pour une création effective, 201 indique explicitement la ressource créée. L’en-tête Location donne son URL canonique, ce qui simplifie la logique côté client (confirmation + emplacement connu). 204 est réservé aux succès sans corps, typiquement sans représentation utile.

Chapitre suivant

API REST back-end : filtrage, tri et pagination des collections

Arrow Right Icon
Couverture de livre électronique gratuite Développement back-end : concevoir une API REST propre
38%

Développement back-end : concevoir une API REST propre

Nouveau cours

8 pages

Téléchargez l'application pour obtenir une certification gratuite et écouter des cours en arrière-plan, même avec l'écran éteint.