Guide pratique : gérer les erreurs d'API
Objectif : mettre en place une gestion des erreurs et une logique de réessai robustes pour les intégrations d'API en production
Durée estimée : 30 à 45 minutes pour la mise en œuvre
Quand l'utiliser : pour mettre en place des intégrations de production capables de gérer de manière transparente les problèmes réseau, l'expiration des authentifications et les interruptions de service.
Codes de retour de l'API
| Coder | Descriptif | Opération |
| 200 | Succès (GET) | Données de réponse du processus |
| 201 | La création a abouti | La ressource a été créée avec succès |
| 202 | La mise à jour a abouti | La ressource a été mise à jour avec succès |
| 400 | Chaîne API incorrecte / Demande non valide | Vérifier le format, les paramètres et le corps de la requête de l' URL |
| 401 | Non autorisé | Réauthentifiez-vous et réessayez |
| 403 | Interdit | Vérifier les autorisations des utilisateurs |
| 404 | Non trouvé | Vérifier que la ressource existe (projet, tableau, période) |
| 500 | Erreur interne du serveur | Réessayez avec un délai d'attente exponentiel; contactez le service d'assistance si le problème persiste |
Comprendre les réponses d'erreur
Les réponses d'erreur renvoient un objet JSON contenant un champ « message » qui décrit le problème :
{
"message": "Poorly formatted date string: 'SOMEDATE:2010'. Ought to be of the form 'granularity':'year'."
}
Messages d'erreur courants :
| Modèle de message | Cause | La solution |
| Chaîne de date mal formatée | Format de période non valide | Utilisez le format Mois:Année (par exemple, January:2024 ) |
| L'utilisateur X n'existe pas | Nom d'utilisateur non valide dans la requête | Vérifier le format du nom d'utilisateur et l'existence du compte |
| Accès refusé | Droits insuffisants | Vérifier que le compte dispose des ensembles d'autorisations requis |
| Table introuvable | La table cible n'existe pas | Vérifier l'orthographe du nom de la table et le projet |
Mise en œuvre de la logique de nouvelle tentative
Les intégrations en production doivent mettre en œuvre un délai d'attente exponentiel en cas de défaillances temporaires :
import requests
import time
import logging
class TBMStudioAPIClient:
"""Production-ready TBM Studio API client with retry logic."""
def __init__(self, customer_id, domain, max_retries=3, base_delay=1.0):
self.customer_id = customer_id
self.domain = domain
self.max_retries = max_retries
self.base_delay = base_delay
self.token = None
self.env_id = None
self.logger = logging.getLogger(__name__)
def authenticate(self, public_key, secret_key):
"""Authenticate and store token."""
url = "https://frontdoor.apptio.com/service/apikeylogin"
response = self._make_request(
"POST", url,
json={"keyAccess": public_key, "keySecret": secret_key},
headers={"Content-Type": "application/json"}
)
self.token = response.cookies.get('apptio-opentoken')
# Get environment ID
env_url = f"https://frontdoor.apptio.com/api/environment/{self.domain}/main"
env_response = self._make_request("GET", env_url)
self.env_id = env_response.json()["id"]
def _make_request(self, method, url, **kwargs):
"""Make HTTP request with retry logic."""
last_exception = None
for attempt in range(self.max_retries):
try:
# Add auth headers if authenticated
if self.token and 'headers' not in kwargs:
kwargs['headers'] = {}
if self.token:
kwargs['headers'].update({
"apptio-opentoken": self.token,
"apptio-current-environment": str(self.env_id) if self.env_id else "",
"app-type": "Flagship",
"app-version": "NA"
})
response = requests.request(method, url, **kwargs)
# Check for retryable status codes
if response.status_code == 401:
self.logger.warning("Token expired, re-authentication required")
raise AuthenticationError("Token expired")
if response.status_code >= 500:
response.raise_for_status() # Will be caught and retried
response.raise_for_status()
return response
except requests.exceptions.RequestException as e:
last_exception = e
if attempt < self.max_retries - 1:
delay = self.base_delay * (2 ** attempt) # Exponential backoff
self.logger.warning(
f"Request failed (attempt {attempt + 1}/{self.max_retries}), "
f"retrying in {delay}s: {e}"
)
time.sleep(delay)
raise last_exception
def upload(self, project, table, time_period, file_path, action="overwrite"):
"""Upload data with error handling."""
import urllib.parse
project_enc = urllib.parse.quote(project)
table_enc = urllib.parse.quote(table)
url = (f"https://{self.customer_id}.apptio.com/biit/api/v1/"
f"{self.domain}/{project_enc}/{table_enc}/{time_period}/{action}")
with open(file_path, 'rb') as f:
response = self._make_request("POST", url, files={'myfile': f})
return response.json()
class AuthenticationError(Exception):
"""Raised when authentication fails or token expires."""
pass
# Usage with error handling
def main():
logging.basicConfig(level=logging.INFO)
client = TBMStudioAPIClient("acme", "acme.com")
try:
client.authenticate("public_key", "secret_key")
result = client.upload(
project="Cost Transparency",
table="GL Data",
time_period="January:2024",
file_path="data.csv"
)
print(f"Upload successful: {result}")
except AuthenticationError:
print("Authentication failed. Check API credentials.")
except requests.exceptions.HTTPError as e:
print(f"API error: {e.response.status_code} - {e.response.text}")
except Exception as e:
print(f"Unexpected error: {e}")Stratégie de rafraîchissement des jetons
Pour les processus de longue durée, mettez en place un rafraîchissement proactif des jetons :
import time
from datetime import datetime, timedelta
class TokenManager:
"""Manage API token lifecycle."""
def __init__(self, client, refresh_margin_minutes=5):
self.client = client
self.refresh_margin = timedelta(minutes=refresh_margin_minutes)
self.token_expiry = None
self.token_lifetime = timedelta(hours=1) # Adjust based on actual expiry
def get_valid_token(self, public_key, secret_key):
"""Get a valid token, refreshing if necessary."""
now = datetime.now()
if (self.token_expiry is None or
now >= self.token_expiry - self.refresh_margin):
self.client.authenticate(public_key, secret_key)
self.token_expiry = now + self.token_lifetime
return self.client.tokenBonnes pratiques en matière d'exploitation forestière
- Enregistrer tous les appels API : inclure l'horodatage, le point de terminaison, l' HTTP la méthode et le statut de la réponse
- Enregistrer les temps de réponse : surveiller toute baisse de performances
- Ne jamais enregistrer les identifiants : masquer les jetons, les clés API et les mots de passe dans les journaux
- Enregistrer les réponses d'erreur : capturer l'intégralité des messages d'erreur à des fins de dépannage
- Utilisez la journalisation structurée : formatez les journaux au format JSON pour faciliter leur analyse et leur interprétation
Avertissement : évitez d'enregistrer des données sensibles telles que des clés API, des jetons ou des informations personnelles identifiables. Utilisez des espaces réservés ou un hachage pour les informations identifiables dans les journaux.
Pièges courants
- Pas de nouvelle tentative en cas d'erreurs 500 : les erreurs serveur sont souvent temporaires. Veillez à toujours mettre en place une nouvelle tentative avec un délai d'attente.
- Non-respect des limites de débit : bien que cela ne soit pas explicitement indiqué, évitez d'envoyer des requêtes à un rythme trop soutenu. Ajouter des délais entre les opérations par lots.
- Absence de gestion de l'expiration des jetons : traitez toujours les réponses 401 en procédant à une nouvelle authentification et en réessayant.
- Analyse d'erreur manquante : analysez et consignez systématiquement le champ « message » des réponses d'erreur afin d'obtenir des diagnostics pertinents.