Gestion des erreurs et codes d'état HTTP

HTTP · Codes d'état · Gestion des erreurs

Gestion des erreurs

L'API utilise des codes d'état HTTP standard et des messages d'erreur structurés au format JSON pour assurer une gestion cohérente des erreurs.


Codes d'état HTTP

CodeStatutDescription
200OKRequête réussie (GET, POST, DELETE)
201CrééNouvelle ressource créée (POST)
400Requête incorrecteCorps de requête non valide, paramètres manquants ou paramètre de requête invalide (depuis la version v2.122.10 sur chaque point de terminaison JSON, voir ci-dessous)
401Non autoriséJoker manquant ou non valide
403Accès interditLe jeton ne dispose pas des périmètres d'accès requis
404IntrouvableRessource introuvable
409ConflitImpossible de supprimer la ressource (contrôle de sécurité)
429Trop de requêtesLimite de requêtes dépassée ; Retry-After indique le temps d'attente en secondes
500Erreur interne du serveurErreur de serveur

Format de réponse d'erreur

HTTP/1.1 401 Unauthorized
X-XoPort-Deny-Reason: deny_token_required
Content-Type: application/json; charset=utf-8

{
  "success": false,
  "api_version": "2.122.13",
  "error": {
    "code": "UNAUTHORIZED",
    "reason": "deny_token_required",
    "message": "Authentication required. JSON/REST routes always require an OAuth2 token and check its scopes; the XML_PORT_IP_FILTER whitelist does not authorize them (changed in API 2.119.0). Obtain a token via POST /oauth/token (client_credentials grant)."
  }
}

Les erreurs liées aux paramètres de requête comportent en outre les champs details[], param et message pour chaque erreur (exemple sur la page API d'exportation). Utilisez error.code comme valeur, et non le texte du message.

Depuis la version v2.119.0, en cas d'appel rejeté, l'API indique le motif dans le champ reason; pour le code 401, elle l'indique également dans l'en-tête X-XoPort-Deny-Reason: par exemple deny_token_required (401, appel sans jeton) ou deny_ip_not_listed (403, adresse non présente dans XOPORT_REST_IP_LIST; uniquement dans le corps).

Codes d'erreur courants

CodeHTTPDescription
INVALID_FILTER_VALUE400Un paramètre de requête contient une valeur invalide ou une valeur de liste (?status[]=1) ; details[] répertorie les erreurs respectives sur param et message. Depuis la version v2.122.10, cela s'applique à tous les points de terminaison JSON, y compris les opérations d'importation et de suppression ; dans ce cas, aucune donnée n'est lue, écrite ou supprimée
INVALID_PAGINATION400limit, offset, page, sort ou order deviennent inutilisables (details[] comme ci-dessus)
INVALID_RANGE400Forme de chemin ou segment de chemin invalide : new:{datum} à un point d’extrémité autre que « clients » et « commandes » (depuis la v2.122.10) ; dans l’export JSON depuis la v2.122.12, également un segment qui n’est ni un nombre entier, ni une plage, ni un sous-itinéraire (/product/42x, /order/42.0, /contracts/10/20). Le message mentionne le segment
FILTER_NOT_APPLICABLE400filtre appliqué à une requête unique ou à une plage d’identifiants sur laquelle il n’a aucun effet ; depuis la v2.122.13, également ?categories_id= associé à un identifiant ou à une plage dans le chemin d’accès de la liste des produits par catégorie (/categories/5/productlist?categories_id=7, /categories/5:10/productlist?categories_id=5). Seule la valeur identique à l’identifiant du chemin d’accès unique reste autorisée
INVALID_STATUS400status_id au point de terminaison de statut PUT /Import/JSON/contracts/{id}/status n’est pas un statut de contrat de la boutique ou n’est pas un nombre entier. Depuis la v2.122.13, error.valid_status_ids indique les ID valides ; jusqu’à la v2.122.12, le point de terminaison vérifiait par rapport aux statuts de commande
UNKNOWN_FILTER200 ou 400Nom de paramètre inconnu lors de l’exportation : avertissement sur warnings[], avec ?_strict=true HTTP 400
MISSING_ID400Suppression sans ID dans le chemin d'accès ; l'ID doit figurer dans le chemin d'accès, par exemple DELETE /…/categories/99
UNAUTHORIZED401Joint d'accès manquant, invalide ou expiré, ou le client est désactivé ; error.reason et l'en-tête X-XoPort-Deny-Reason indiquent la raison
IP_NOT_ALLOWED403Appel provenant d'une adresse qui ne figure pas dans XOPORT_REST_IP_LIST (depuis la v2.119.0)
INSUFFICIENT_PERMISSIONS403Le jeton ne dispose pas de la portée requise pour le point de terminaison (message: « Required scope: … »)
NOT_FOUND404Ressource introuvable : requête individuelle sans résultat vers /product/{id}, /products/{id} et /order_status/{id} (depuis la v2.122.10), ainsi que vers les médias, les pièces jointes aux tickets, les documents justificatifs et DELETE pour un identifiant inconnu. Les autres requêtes individuelles renvoient un code HTTP 200 et un corps de réponse vide ( data). Une URL de base erronée renvoie également un code 404 NOT_FOUND
HAS_SUBCATEGORIES409La catégorie comporte des sous-catégories
HAS_PRODUCTS409La catégorie contient des produits
HAS_NEWS409La catégorie « Actualités » contient des articles
HAS_ORDERS409Le client a passé des commandes
RATE_LIMIT_EXCEEDED429Limite de requêtes du client dépassée ; depuis la version v2.122.11 avec error.retry_after

Le point de terminaison des jetons POST /oauth/token répond au format OAuth2 (error, error_description), et non avec ces codes : voir « Erreur au niveau du point de terminaison des jetons » ci-dessous.

Une modification de la portée sur le client ne prend effet qu’avec le prochain jeton : un jeton déjà émis conserve les portées qui lui étaient attribuées lors de son émission jusqu’à son expiration, c’est-à-dire pendant une heure maximum. Pour révoquer immédiatement l’accès dans le backend, utilisez « Révoquer les jetons » dans le menu d’actions du client (à partir de la version 4.9.168 de la boutique) ; dans les versions antérieures, désactivez puis réactivez le client ; le secret reste inchangé. En revanche, une modification de la limite de requêtes prend effet immédiatement.

Le journal d’utilisation du client ne mentionne pas les interruptions de l’exportation avec un code HTTP 400 avant le début d’une requête (erreurs de filtrage, de navigation et de chemin d’accès), ni la réponse 429. Si vous recherchez une erreur à cet endroit, vous ne trouverez donc pas ces cas ; les codes 400 sont néanmoins pris en compte dans la limite de requêtes.

Modifié dans la version v2.122.10 :
  • Les valeurs invalides et les valeurs de liste des paramètres de requête génèrent un code HTTP 400 « INVALID_FILTER_VALUE » sur chaque point de terminaison JSON (lors des opérations d’importation et de suppression, respectivement « … Nothing was written » et « … Nothing was deleted »). Jusqu’à la v2.122.9, un tel filtre était généralement ignoré, et la réponse était renvoyée sans filtrage avec un code HTTP 200. Règles et exemples : API d’exportation.
  • GET /product/{id} et les valeurs « /products/{id} » pour un article supprimé ou jamais attribué génèrent un code HTTP 404 NOT_FOUND (message « Product with id=… not found ») au lieu d’un code HTTP 200 avec un corps de réponse vide data. Si vous interrompez l’opération en cas de réponse autre qu’un code 2xx, ce cas est désormais traité comme une erreur.
  • new:{datum} Sur un autre point de terminaison que « Clients » et « Commandes », cela renvoie un code HTTP 400 INVALID_RANGE.
Modifié dans la version v2.122.12 :
  • Un segment de chemin invalide dans l’exportation JSON génère un code HTTP 400 « INVALID_RANGE ». Jusqu’à la version v2.122.11, le code HTTP 200 renvoyait la liste paginée, et des formats tels que 1e3, 4.2 ou +5 étaient considérés comme des identifiants. Règles et exemples : API d’exportation, section « Segment de chemin ».
  • Les espaces à la fin d’une valeur de requête ou d’un identifiant dans le chemin ne sont plus pris en compte. Certaines requêtes qui, jusqu’à présent, contournaient la validation des valeurs tout en fonctionnant différemment, renvoient désormais une erreur ou un résultat différent ; par exemple, ?_strict=true%0A avec un filtre inconnu renvoie désormais un code HTTP 400.
Modifié dans la version v2.122.13 :
  • Statut du contrat : le point de terminaison de statut PUT /Import/JSON/contracts/{id}/status vérifie status_id par rapport aux statuts de contrat de la boutique plutôt que par rapport aux statuts de commande. Un identifiant qui n’existe qu’en tant que statut de commande renvoie désormais un code HTTP 400 INVALID_STATUS; un statut de contrat qui n’existe pas également en tant que statut de commande est enregistré. Récupérez les identifiants à partir de contracts_statuses[], de GET /Export/JSON/contracts ou de error.valid_status_ids, jamais à partir de /order_statuses. L’importation des contrats signale un identifiant non valide (par exemple, un identifiant correspondant uniquement à un statut de commande) avec l’avertissement CONTRACT_STATUS_UNKNOWN: l’enregistrement est écrit, mais pas le statut.
  • Liste des produits par catégorie : ?categories_id=, associé à un ID ou à une plage dans le chemin d'accès, renvoie une erreur HTTP 400 FILTER_NOT_APPLICABLE, sauf si la valeur est identique à celle de l'ID du chemin d'accès unique. Jusqu'à la version v2.122.12, la catégorie était renvoyée sous forme de paramètre avec un code HTTP 200. Envoyez la catégorie soit dans le chemin d'accès, soit en tant que paramètre.

Limite de requêtes dépassée (429)

Depuis la v2.122.11, Retry-After indique le nombre de secondes avant qu’une nouvelle requête ne soit acceptée (de 1 à 3 600), et le corps de la requête contient le même nombre que error.retry_after. À cela s’ajoutent les en-têtes X-RateLimit-Limit, X-RateLimit-Remaining et X-RateLimit-Reset, que chaque réponse envoyée à un client soumis à une limite de requêtes contient (voir Clients OAuth2).

HTTP/1.1 429 Too Many Requests
Retry-After: 1260
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1792327500
Content-Type: application/json; charset=utf-8

{
  "success": false,
  "api_version": "2.122.13",
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit of 5000 requests per 3600 s (sliding window) exceeded.",
    "retry_after": 1260
  }
}
  • La valeur déterminante pour la prochaine tentative est Retry-After. La plupart du temps, « maintenant + Retry-After» correspond exactement à X-RateLimit-Reset; si la limite d’un client vient d’être réduite, c’est Retry-After qui s’applique.
  • Jusqu’à la version v2.122.10 (boutique jusqu’à la version 4.9.170), Retry-After était toujours égal à 3600, il n’y avait pas de champ retry_after et le message indiquait « Rate limit of 5000 requests/hour exceeded. ». Tenez compte de la valeur de error.code, jamais du texte du message.
  • Une requête rejetée (429) ne compte pas. Il faut répartir un grand nombre de requêtes de manière uniforme sur l’heure et ralentir dès que X-RateLimit-Remaining atteint 0, au lieu d’attendre le 429.

Erreur au niveau du point de terminaison du jeton

POST /oauth/token répond, dans le format OAuth2, par error et error_description. Depuis la version v2.122.11, une portée demandée illisible (UTF-8 non valide ou une liste telle que scope[]=…) génère un code HTTP 400 invalid_scope; aucun jeton n’est émis. client_id ou client_secret sous forme de liste génèrent un code HTTP 400 invalid_request. Retry-After indique le délai d'attente (de 1 à 300 secondes) propre au point de terminaison du jeton. Toutes les erreurs liées au point de terminaison du jeton sont répertoriées sous « Clients OAuth2 ».

Bonnes pratiques

Vérifiez toujours le champ « success » dans la réponse et implémentez une logique de réessai pour les erreurs 429 et 5xx.