Formats de Réponse
Comprendre les Structures de Réponse de l’API
Découvrez les formats de réponse mindzieAPI, les codes de statut, les modèles de gestion des erreurs et les structures de données pour construire des intégrations robustes.
Format de Réponse Standard
Toutes les réponses mindzieAPI suivent un format JSON cohérent avec des structures prévisibles :
Réponse Réussie
{
"data": {
// Données principales de la réponse
},
"metadata": {
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "req_12345",
"version": "1.0.0"
},
"pagination": {
// Présent pour les réponses paginées
"currentPage": 1,
"totalPages": 5,
"totalItems": 100,
"itemsPerPage": 20,
"hasNext": true,
"hasPrevious": false
}
}
Réponse d’Erreur
{
"error": {
"code": "validation_failed",
"message": "Échec de la validation de la requête",
"details": {
"field": "datasetId",
"reason": "Format GUID invalide"
},
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "req_12345"
}
}
Types de Réponses
Réponses Réussies
Codes de statut HTTP 2xx avec données JSON structurées et métadonnées.
Réponses d’Erreur
Codes de statut HTTP 4xx/5xx avec informations détaillées sur l’erreur.
Pagination
Format de pagination cohérent pour les réponses avec grands ensembles de données.
Codes de Statut HTTP
Codes de Succès (2xx)
| Code | Statut | Description | Usage |
|---|---|---|---|
200 |
OK | Requête réussie, données retournées | Requêtes GET, opérations réussies |
201 |
Créé | Ressource créée avec succès | Requêtes POST créant de nouvelles ressources |
202 |
Accepté | Requête acceptée pour traitement asynchrone | Opérations longues, tâches en file d’attente |
204 |
Pas de Contenu | Requête réussie, pas de données retournées | Requêtes DELETE, mises à jour sans données retournées |
Codes d’Erreur Client (4xx)
| Code | Statut | Description | Causes Courantes |
|---|---|---|---|
400 |
Mauvaise Requête | Format ou paramètres de requête invalides | En-têtes manquants, JSON invalide, données mal formées |
401 |
Non Autorisé | Authentification requise ou échouée | Token manquant/invalide, identifiants expirés |
403 |
Interdit | Authentification valide mais permissions insuffisantes | Accès utilisateur limité, mauvais tenant/projet |
404 |
Non Trouvé | Ressource demandée inexistante | Point d’accès invalide, ID de ressource inexistant |
422 |
Entité Non Traitée | Format valide mais validation de logique métier échouée | Règles métier invalides, violations de contraintes |
429 |
Trop de Requêtes | Limite de taux dépassée | Trop d’appels API dans la fenêtre temporelle |
Codes d’Erreur Serveur (5xx)
| Code | Statut | Description | Action |
|---|---|---|---|
500 |
Erreur Interne Serveur | Erreur serveur inattendue | Réessayer avec backoff exponentiel |
502 |
Mauvaise Passerelle | Erreur du service en amont | Vérifier le statut du service, réessayer plus tard |
503 |
Service Indisponible | Service temporairement indisponible | Réessayer après délai, vérifier maintenance |
504 |
Délai Passerelle Expiré | Délai d’attente de la requête dépassé | Augmenter le timeout, optimiser la requête |
Modèles Courants de Réponse
Réponse d’une Ressource Unique
{
"actionId": "87654321-4321-4321-4321-210987654321",
"actionType": "analyze",
"status": "completed",
"startTime": "2024-01-15T10:30:00Z",
"endTime": "2024-01-15T10:32:15Z",
"duration": 135,
"result": {
"outputId": "98765432-8765-4321-4321-987654321098",
"recordsProcessed": 10000
}
}
Réponse de Collection avec Pagination
{
"actions": [
{
"actionId": "87654321-4321-4321-4321-210987654321",
"actionType": "analyze",
"status": "completed"
},
{
"actionId": "11111111-2222-3333-4444-555555555555",
"actionType": "export",
"status": "processing"
}
],
"pagination": {
"currentPage": 1,
"totalPages": 5,
"totalItems": 100,
"itemsPerPage": 20,
"hasNext": true,
"hasPrevious": false,
"links": {
"first": "/api/Action/history?page=1&limit=20",
"next": "/api/Action/history?page=2&limit=20",
"last": "/api/Action/history?page=5&limit=20"
}
}
}
Réponse d’Opération Asynchrone
{
"operationId": "op_12345678-1234-1234-1234-123456789012",
"status": "processing",
"progress": {
"percentage": 45,
"currentStep": "data_analysis",
"totalSteps": 5,
"estimatedCompletion": "2024-01-15T10:35:00Z"
},
"trackingUrl": "/api/Execution/status/op_12345678-1234-1234-1234-123456789012",
"message": "Analyse du jeu de données en cours..."
}
Détails des Réponses d’Erreur
Erreur de Validation
{
"error": {
"code": "validation_failed",
"message": "Échec de la validation de la requête",
"details": {
"errors": [
{
"field": "datasetId",
"code": "invalid_format",
"message": "Doit être un GUID valide"
},
{
"field": "parameters.timeout",
"code": "out_of_range",
"message": "Doit être compris entre 1 et 3600 secondes"
}
]
},
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "req_12345"
}
}
Erreur d’Authentification
{
"error": {
"code": "invalid_token",
"message": "Le token d’accès fourni est invalide ou expiré",
"details": {
"tokenType": "bearer",
"expiresAt": "2024-01-15T09:00:00Z",
"suggestion": "Veuillez rafraîchir votre token d’accès"
},
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "req_12345"
}
}
Erreur de Limitation de Taux
{
"error": {
"code": "rate_limit_exceeded",
"message": "Limite de taux dépassée pour ce point d’accès",
"details": {
"limit": 100,
"remaining": 0,
"resetTime": "2024-01-15T11:00:00Z",
"retryAfter": 1800
},
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "req_12345"
}
}
En-têtes de Réponse
En-têtes Standards
| En-tête | Description | Exemple |
|---|---|---|
Content-Type |
Format de la réponse | application/json; charset=utf-8 |
X-Request-Id |
Identifiant unique de la requête | req_12345678 |
X-Response-Time |
Temps de traitement serveur | 145ms |
X-API-Version |
Version de l’API utilisée | 1.0.0 |
En-têtes de Limitation de Taux
| En-tête | Description | Exemple |
|---|---|---|
X-RateLimit-Limit |
Nombre maximal de requêtes par fenêtre | 100 |
X-RateLimit-Remaining |
Requêtes restantes dans la fenêtre | 95 |
X-RateLimit-Reset |
Heure de réinitialisation de la fenêtre | 1642251600 |
Retry-After |
Secondes à attendre avant de réessayer | 3600 |
Bonnes Pratiques pour la Gestion des Erreurs
Exemple JavaScript
async function handleAPIResponse(response) {
// Vérifie si la réponse est correcte
if (!response.ok) {
const errorData = await response.json();
switch (response.status) {
case 400:
throw new ValidationError(errorData.error.message, errorData.error.details);
case 401:
throw new AuthenticationError('Échec d’authentification');
case 403:
throw new AuthorizationError('Permissions insuffisantes');
case 404:
throw new NotFoundError('Ressource non trouvée');
case 429:
const retryAfter = response.headers.get('Retry-After');
throw new RateLimitError(`Limite de taux atteinte. Réessayer après ${retryAfter} secondes`);
case 500:
case 502:
case 503:
case 504:
throw new ServerError('Erreur serveur survenue. Veuillez réessayer.');
default:
throw new APIError(`Erreur inattendue : ${response.status}`);
}
}
return await response.json();
}
// Utilisation avec logique de retry
async function apiCallWithRetry(url, options, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
return await handleAPIResponse(response);
} catch (error) {
if (error instanceof RateLimitError) {
const retryAfter = parseInt(error.retryAfter) || Math.pow(2, attempt);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
if (error instanceof ServerError && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // Backoff exponentiel
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
Exemple Python
import requests
import time
from typing import Dict, Any
class APIError(Exception):
def __init__(self, message: str, status_code: int = None, details: Dict = None):
super().__init__(message)
self.status_code = status_code
self.details = details
def handle_api_response(response: requests.Response) -> Dict[str, Any]:
"""Gère la réponse de l’API avec gestion appropriée des erreurs"""
if response.ok:
return response.json()
try:
error_data = response.json()
except ValueError:
error_data = {"error": {"message": response.text}}
error_info = error_data.get("error", {})
message = error_info.get("message", f"HTTP {response.status_code}")
details = error_info.get("details", {})
if response.status_code == 429:
retry_after = response.headers.get('Retry-After', '60')
raise APIError(f"Limite de taux atteinte. Réessayer après {retry_after} secondes",
response.status_code, details)
elif response.status_code >= 500:
raise APIError(f"Erreur serveur : {message}", response.status_code, details)
elif response.status_code >= 400:
raise APIError(f"Erreur client : {message}", response.status_code, details)
raise APIError(f"Erreur inattendue : {message}", response.status_code, details)
def api_call_with_retry(url: str, method: str = 'GET', max_retries: int = 3, **kwargs) -> Dict[str, Any]:
"""Effectue un appel API avec logique de retry automatique"""
for attempt in range(1, max_retries + 1):
try:
response = requests.request(method, url, **kwargs)
return handle_api_response(response)
except APIError as e:
if e.status_code == 429:
retry_after = int(e.details.get('retryAfter', 60))
time.sleep(retry_after)
continue
elif e.status_code >= 500 and attempt < max_retries:
delay = 2 ** attempt # Backoff exponentiel
time.sleep(delay)
continue
raise
raise APIError(f"Nombre maximal de retries ({max_retries}) dépassé")
Étapes Suivantes
Maintenant que vous comprenez les formats de réponse, explorez des sections API spécifiques comme Actions, Blocs, ou Jeux de Données pour voir ces modèles en action.