9 min de lecture

RFC 9457 (Problem Details) et Symfony : erreurs API lisibles et stables

Format application/problem+json, champs type/title/status/instance, mapping des exceptions Symfony vers des réponses cohérentes pour les clients.

  • API
  • HTTP
  • Symfony
Schéma : réponse d’erreur structurée Problem+JSON application/problem+json "type": "https://…/errors/invalid", "title": "Validation Failed", "status": 422

Pourquoi ne pas renvoyer du JSON « maison » à chaque fois ?

Sans convention, chaque endpoint invente ses clés (error, message, errors[]…). Les SDK et les intégrations deviennent fragiles. Problem Details donne un contrat minimal reconnu par les outils et les humains.

Champs utiles en pratique

  • type : URI (souvent sous votre domaine) identifiant la classe d’erreur — stable dans le temps.
  • title : libellé court, lisible ; peut être localisé si vous versionnez l’API.
  • status : redondance explicite avec le code HTTP (422, 404, 409…).
  • detail : message contextuel (éviter les données sensibles ; préférer un identifiant de corrélation).
  • instance : URI de la requête fautive ou id de trace pour le support.

Exemple de corps 422 (validation)

{
  "type": "https://api.example.com/problems/validation-failed",
  "title": "Validation Failed",
  "status": 422,
  "detail": "One or more fields are invalid.",
  "instance": "/v1/orders",
  "errors": [
    { "field": "email", "code": "invalid_format" }
  ]
}

Le tableau errors est une extension courante (non obligatoire dans la RFC) : documentez-la dans votre spec OpenAPI.

Symfony : JsonResponse typée

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;

$body = [ ‘type’ => ‘https://api.example.com/problems/not-found’, ‘title’ => ‘Resource Not Found’, ‘status’ => Response::HTTP_NOT_FOUND, ‘detail’ => ‘No order matches this id.’, ‘instance’ => $request->getRequestUri(), ];

return new JsonResponse( $body, Response::HTTP_NOT_FOUND, [‘Content-Type’ => ‘application/problem+json’] );

Listener d’exception

Centralisez la traduction ValidationFailedException → 422, EntityNotFound → 404, erreurs métier → 409, etc. Gardez un seul format de sortie pour toute l’API.

FAQ

Faut-il toujours utiliser application/problem+json ?

Recommandé pour les erreurs ; pour les réponses 2xx vous gardez votre schéma métier. L’important est la cohérence des erreurs sur tout le périmètre API.

Et les erreurs Symfony Validator en tableau ?

Normalisez-les dans une extension (violations ou errors) avec codes stables, pas seulement les messages traduits — les clients s’appuient sur les codes.