RFC 9457 problem+json standardise les réponses d'erreur sous forme d'objet JSON avec type, title, status, detail et instance, servi en application/problem+json — pour que les clients analysent les erreurs uniformément. La pagination découpe les grandes collections en pages à l'aide d'un cursor (stable) ou d'un offset (simple), avec les liens next/prev portés dans l'en-tête Link de RFC 8288.
problem+json : un objet d'erreur standard
Des formes d'erreur sur mesure forcent chaque client à traiter en cas particulier chaque API. RFC 9457 (qui rend obsolète RFC 7807) fixe un format d'erreur JSON unique et extensible, servi avec le type de média application/problem+json pour qu'un client sache l'analyser comme un problème. L'objet a cinq membres standard : type, un URI identifiant la classe de problème et faisant office de lien vers une documentation humaine ; title, un résumé court, stable et lisible par un humain de cette classe ; status, le code de statut HTTP répercuté dans le corps ; detail, une explication lisible par un humain propre à cette occurrence ; et instance, un URI identifiant cette occurrence précise. Tout membre supplémentaire (un tableau d'erreurs de validation, un id de requête, un indice de reprise) étend l'objet sans casser les membres standard, si bien qu'un client générique lit les champs communs et un client spécifique lit aussi les extensions.
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{
"type": "https://example/problems/invalid-destination",
"title": "Invalid destination number",
"status": 422,
"detail": "The field 'to' is not a valid E.164 number.",
"instance": "/v1/messages/req-01HZY9",
"errors": [ { "field": "to", "issue": "not_e164" } ]
}Type, title et instance
La discipline qui rend problem+json utile est de garder type et title stables et orientés machine, tout en laissant detail et instance varier par occurrence. Un client devrait bifurquer sur type (un URI stable), jamais sur la prose de detail ou title, car detail est libre de changer et peut être localisé. L'URI de type se résout idéalement vers la documentation de cette classe d'erreur, pour qu'un développeur qui le suit atterrisse sur une explication et un remède. instance identifie la requête échouée précise — utile à citer dans un ticket de support — et reflète souvent un id de requête que l'API renvoie aussi dans un en-tête. Comme type et title identifient la classe et que status/detail/instance décrivent l'occurrence, un même type de problème peut être réutilisé pour de nombreux échecs sans ambiguïté.
Taxonomie des erreurs : rejouable vs terminal
Au-delà de la forme, le client a besoin de savoir quoi faire, et la classe du code de statut y répond. Un 4xx est une erreur client : la requête telle qu'envoyée ne réussira jamais, si bien que la rejouer telle quelle est inutile — le correctif est de corriger la requête (400/422 pour une entrée invalide, 401/403 pour l'authentification, 404 pour une ressource absente, 409 pour un conflit). Un 5xx est une erreur serveur ou amont : la requête pourrait bien réussir plus tard, si bien qu'elle est rejouable avec backoff — idéalement sous la même Idempotency-Key pour que la reprise ne puisse doubler l'envoi. 429 est le cas transitoire particulier : rejouable, mais seulement après le délai Retry-After. Encoder cela dans le code de statut (et le renforcer avec un type de problème) permet à un client de décider automatiquement entre corriger-et-réémettre, backoff-et-rejouer, et abandonner.
| Classe | Signification | Réitérable ? | Action du client |
|---|---|---|---|
| 400 / 422 | Entrée malformée/invalide | No | Corrigez la requête, puis renvoyez-la |
| 401 / 403 | Non authentifié / interdit | No | Corriger les informations d'identification ou la portée |
| 404 | Ressource introuvable | No | Corrigez l'identifiant |
| 409 | Conflit d'état (ex. réutilisation de clés) | No | Résoudre le conflit |
| 429 | Limite de débit atteinte | Oui (différé) | Attendre le délai Retry-After, puis réessayer |
| 500 / 502 / 503 | Défaillance du serveur/de la chaîne en amont | Oui | Backoff + nouvelle tentative avec la même clé d'idempotence |
Pagination : cursor vs offset
Un endpoint de collection qui pourrait renvoyer des millions de messages doit paginer. Les deux modèles courants arbitrent entre simplicité et exactitude. La pagination par offset (?limit=50&offset=100) est triviale et laisse un client sauter à une page arbitraire, mais elle est instable sur une collection changeante : si des lignes sont insérées ou supprimées entre les pages, les éléments se décalent et le client saute ou répète des enregistrements — et les offsets profonds deviennent lents car le magasin doit parcourir et écarter tout ce qui précède. La pagination par cursor (keyset) remet au client un cursor opaque qui encode la position du dernier élément vu (par exemple un id et un timestamp) ; la requête suivante demande les lignes après ce cursor. Elle est stable sous écritures concurrentes et reste rapide à toute profondeur car la requête se positionne directement sur le cursor, au prix de ne pas permettre de sauts de page arbitraires. Pour les ressources à haut volume et à forte croissance comme les journaux de messages, la pagination par cursor est le bon choix par défaut.
| Propriété | Décalage | Curseur (keyset) |
|---|---|---|
| Requête | ?limit=50&offset=100 | ?limit=50&after=<cursor> |
| Saut de page arbitraire | Oui | Non (séquentiel uniquement) |
| Stable en cas d'insertions/suppressions | Non — les éléments se décalent | Oui |
| Performances des pages profondes | Se dégrade (parcours + rejet) | Constant (accès direct au curseur) |
| Idéal pour | Petites collections stables | Journaux volumineux, principalement en ajout |
L'en-tête Link (RFC 8288)
Plutôt que d'enfouir les URL next/previous dans le corps, une API peut les porter dans l'en-tête Link standard défini par RFC 8288 (Web Linking), à l'aide de types de relation enregistrés — next, prev, first, last. Le client suit le lien rel="next" pour avancer sans construire d'URL lui-même ni connaître le schéma de cursor, ce qui garde le mécanisme de pagination entièrement contrôlé par le serveur et lui permet d'évoluer (offset aujourd'hui, cursor demain) sans casser les clients. Une approche ceinture-et-bretelles courante reflète aussi les liens dans le corps JSON pour les clients qui ne lisent pas les en-têtes, mais l'en-tête Link est leur emplacement standardisé, au niveau transport, et c'est le même mécanisme qu'un navigateur ou un client d'API bien élevé comprend déjà.
HTTP/1.1 200 OK
Content-Type: application/json
Link: </v1/messages?after=01HZY8Q3M6&limit=50>; rel="next",
</v1/messages?limit=50>; rel="first"
{ "data": [ /* up to 50 messages */ ],
"page": { "count": 50, "has_more": true } }Limites et valeurs par défaut de taille de page
La taille de page doit être bornée côté serveur, non laissée au client, sinon une seule requête pour une page énorme devient un vecteur de déni de service et une source de latence imprévisible. Le motif est une valeur par défaut sensée (utilisée quand le client omet limit) et un maximum strict auquel le serveur borne silencieusement — un client qui demande davantage reçoit simplement le maximum. Annoncer à la fois la taille de page effective et s'il reste des pages (un drapeau has_more ou la présence d'un lien rel="next") permet à un client de boucler sans risque : continuer à suivre next jusqu'à ce qu'il n'y ait plus de next. Combiné à la pagination par cursor et aux erreurs problem+json, cela donne une API de collection prévisible à consommer à toute échelle. BroadNet documente ses codes d'erreur et sa pagination concrets pour les développeurs.
Questions fréquemment posées
Qu'est-ce que problem+json et pourquoi l'utiliser ?
problem+json (RFC 9457, qui rend obsolète RFC 7807) est un format d'erreur JSON standard servi en application/problem+json, avec les membres type, title, status, detail et instance. Il permet à chaque client d'analyser les erreurs de la même façon au lieu de traiter en cas particulier une forme sur mesure par API, et il est extensible pour que des membres supplémentaires puissent être ajoutés sans casser les membres standard.
Sur quel champ problem+json un client devrait-il bifurquer ?
type — un URI stable identifiant la classe d'erreur. title et detail sont lisibles par un humain et peuvent changer ou être localisés, si bien que le code ne devrait jamais brancher sur leur prose. L'URI de type se résout idéalement vers la documentation de cette erreur, tandis que status, detail et instance décrivent l'occurrence précise.
Comment un client sait-il s'il faut rejouer une erreur ?
À partir de la classe de statut. Un 4xx est une erreur client qui ne réussira pas telle quelle, donc corrigez la requête plutôt que de rejouer. Un 5xx est un échec serveur/amont qui peut réussir plus tard, donc rejouez avec backoff sous la même clé d'idempotence. 429 est rejouable mais seulement après le délai Retry-After.
Quelle est la différence entre pagination par cursor et par offset ?
L'offset (?limit&offset) est simple et permet de sauter à n'importe quelle page mais est instable quand des lignes sont insérées ou supprimées entre les requêtes et ralentit aux offsets profonds. La pagination par cursor (keyset) passe un marqueur opaque du dernier élément vu et demande les lignes après lui — stable sous écritures concurrentes et rapide à toute profondeur, mais séquentielle seulement. Le cursor est le bon choix par défaut pour de grands journaux de messages.
Comment l'en-tête Link de RFC 8288 aide-t-il la pagination ?
Il porte les URL des pages next, prev, first et last dans un en-tête standard à l'aide de types de relation enregistrés, pour que le client suive rel="next" pour avancer sans construire d'URL ni connaître le schéma de cursor. Cela garde le mécanisme de pagination contrôlé par le serveur et libre d'évoluer, et c'est un mécanisme que les clients HTTP génériques comprennent déjà.
Pourquoi plafonner la taille de page côté serveur ?
Parce qu'une taille de page non bornée laisse une requête tirer un jeu de résultats énorme, ce qui est un risque de déni de service et donne une latence imprévisible. Le serveur applique une valeur par défaut quand limit est omis et borne silencieusement à un maximum strict, et il annonce s'il reste des pages pour que les clients puissent boucler en suivant next jusqu'à son absence.
Spécifications sources
Les normes publiées sur lesquelles repose cette page. S'ouvre dans un nouvel onglet.