Introduction
Ce tutoriel vous guidera à travers la gestion des différents codes d'état HTTP dans les requêtes Python. Les codes d'état HTTP sont essentiels pour comprendre si une requête web a réussi ou échoué, et comment répondre correctement aux différents scénarios. À la fin de ce lab, vous apprendrez à implémenter une gestion des erreurs robuste dans vos applications Python qui interagissent avec les services web.
Vous commencerez par comprendre les codes d'état HTTP, puis vous développerez progressivement vos compétences en implémentant des techniques de gestion des erreurs de base et avancées. Ces connaissances sont cruciales pour développer des applications web Python fiables qui gèrent avec élégance les différentes réponses du serveur.
Comprendre les codes d'état HTTP et la configuration
Que sont les codes d'état HTTP ?
Les codes d'état HTTP sont des nombres à trois chiffres renvoyés par un serveur en réponse à une requête du client. Ils indiquent si une requête HTTP spécifique a été menée à bien. Ces codes sont regroupés en cinq catégories :
- 1xx (Informationnel) : La requête a été reçue et le processus continue
- 2xx (Succès) : La requête a été reçue, comprise et acceptée avec succès
- 3xx (Redirection) : Une action supplémentaire doit être entreprise pour compléter la requête
- 4xx (Erreur client) : La requête contient une mauvaise syntaxe ou ne peut pas être satisfaite
- 5xx (Erreur serveur) : Le serveur n'a pas réussi à satisfaire une requête apparemment valide
Les codes d'état courants que vous rencontrerez incluent :
| Code d'état | Nom | Description |
|---|---|---|
| 200 | OK | Requête réussie |
| 201 | Created | Requête réussie et ressource créée |
| 400 | Bad Request | Le serveur ne peut pas traiter la requête |
| 401 | Unauthorized | L'authentification est requise |
| 404 | Not Found | La ressource demandée n'a pas été trouvée |
| 500 | Internal Server Error | Le serveur a rencontré une condition inattendue |
Configuration de votre environnement Python
Avant de commencer à gérer les codes d'état HTTP, installons la bibliothèque Python requests et créons un script de test simple.
- Ouvrez un terminal dans l'environnement LabEx et exécutez :
pip install requests
- Créez un nouveau répertoire pour les fichiers de notre projet :
mkdir -p ~/project/http_status_lab
cd ~/project/http_status_lab
- En utilisant le WebIDE, créez un nouveau fichier appelé
test_request.pydans le répertoirehttp_status_labavec ce code de base :
import requests
def make_request(url):
"""Make a basic GET request to the specified URL."""
response = requests.get(url)
print(f"Status Code: {response.status_code}")
print(f"Response: {response.text[:100]}...") ## Print first 100 characters
return response
## Test with a working URL
response = make_request("https://httpbin.org/status/200")
print(f"Request was successful: {response.ok}")
- Exécutez le script de test pour le voir en action :
python test_request.py
Vous devriez voir une sortie similaire à celle-ci :
Status Code: 200
Response: ...
Request was successful: True
Cela confirme que vous avez configuré avec succès l'environnement Python et que vous pouvez effectuer des requêtes HTTP de base. Dans les prochaines étapes, vous apprendrez à gérer différents codes d'état HTTP et à implémenter des techniques de gestion des erreurs plus avancées.
Gestion des codes d'état HTTP de base
Maintenant que vous comprenez ce que sont les codes d'état HTTP et que vous avez configuré votre environnement, implémentons une gestion des erreurs de base pour les codes d'état courants.
Création d'un outil de test des codes d'état
Tout d'abord, créons un outil qui nous permettra de tester différents codes d'état HTTP. Le site web httpbin.org fournit des points de terminaison qui renvoient des codes d'état spécifiques, ce qui est parfait pour nos tests.
Créez un nouveau fichier appelé status_code_tester.py dans votre répertoire http_status_lab avec le code suivant :
import requests
import sys
def test_status_code(status_code):
"""Test a specific HTTP status code using httpbin.org."""
url = f"https://httpbin.org/status/{status_code}"
try:
response = requests.get(url)
print(f"Status Code: {response.status_code}")
print(f"Response OK: {response.ok}")
return response
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
return None
if __name__ == "__main__":
## Get status code from command line or use default
status_code = sys.argv[1] if len(sys.argv) > 1 else "200"
test_status_code(status_code)
Ce script effectue une requête vers httpbin.org avec un code d'état spécifique et affiche le résultat. Vous pouvez l'exécuter avec un argument de ligne de commande pour tester différents codes d'état.
Test de différents codes d'état
Testons notre script avec différents codes d'état pour voir comment la bibliothèque requests les gère :
- Testez une requête réussie (200 OK) :
python status_code_tester.py 200
Sortie attendue :
Status Code: 200
Response OK: True
- Testez une erreur client (404 Not Found) :
python status_code_tester.py 404
Sortie attendue :
Status Code: 404
Response OK: False
- Testez une erreur serveur (500 Internal Server Error) :
python status_code_tester.py 500
Sortie attendue :
Status Code: 500
Response OK: False
Gestion des catégories de codes d'état de base
Maintenant, créons un script plus complet qui gère correctement différentes catégories de codes d'état. Créez un nouveau fichier appelé basic_handler.py dans votre répertoire http_status_lab :
import requests
def handle_response(url):
"""Handle different HTTP status codes with basic error handling."""
try:
response = requests.get(url)
## Check status code category
if 200 <= response.status_code < 300:
print(f"Success! Status code: {response.status_code}")
return response
elif 300 <= response.status_code < 400:
print(f"Redirection! Status code: {response.status_code}")
## For redirection, you might want to follow the redirect
return response
elif 400 <= response.status_code < 500:
print(f"Client error! Status code: {response.status_code}")
## Handle client errors
return response
elif 500 <= response.status_code < 600:
print(f"Server error! Status code: {response.status_code}")
## Handle server errors
return response
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
return None
## Test with different status codes
print("Testing 200 OK:")
handle_response("https://httpbin.org/status/200")
print("\nTesting 404 Not Found:")
handle_response("https://httpbin.org/status/404")
print("\nTesting 500 Internal Server Error:")
handle_response("https://httpbin.org/status/500")
Exécutez le script pour voir comment il gère différents codes d'état :
python basic_handler.py
Sortie attendue :
Testing 200 OK:
Success! Status code: 200
Testing 404 Not Found:
Client error! Status code: 404
Testing 500 Internal Server Error:
Server error! Status code: 500
Ce gestionnaire de base regroupe les codes d'état en catégories et prend différentes mesures en fonction de la catégorie. Il s'agit d'un modèle courant dans les applications web Python, qui vous permet de gérer différents types de réponses de manière appropriée.
Implémentation d'une gestion des erreurs avancée
Maintenant que vous comprenez les bases de la gestion des codes d'état HTTP, implémentons des techniques de gestion des erreurs plus avancées pour vos applications Python.
Utilisation de raise_for_status()
La bibliothèque requests fournit une méthode pratique appelée raise_for_status() qui lève une exception pour les codes d'état 4xx et 5xx. C'est un moyen simple mais efficace de gérer les erreurs dans votre code.
Créez un nouveau fichier appelé raise_for_status_example.py dans votre répertoire http_status_lab :
import requests
def fetch_data(url):
"""Fetch data from a URL with error handling using raise_for_status()."""
try:
response = requests.get(url)
## This will raise an HTTPError if the HTTP request returned an unsuccessful status code
response.raise_for_status()
## If we get here, the request was successful
print(f"Success! Status code: {response.status_code}")
return response.json() if 'application/json' in response.headers.get('Content-Type', '') else response.text
except requests.exceptions.HTTPError as e:
print(f"HTTP Error: {e}")
return None
except requests.exceptions.ConnectionError as e:
print(f"Connection Error: {e}")
return None
except requests.exceptions.Timeout as e:
print(f"Timeout Error: {e}")
return None
except requests.exceptions.RequestException as e:
print(f"Request Exception: {e}")
return None
## Test with different URLs
print("Testing successful request:")
data = fetch_data("https://httpbin.org/json")
print(f"Received data type: {type(data)}")
print("\nTesting 404 error:")
data = fetch_data("https://httpbin.org/status/404")
print(f"Received data: {data}")
print("\nTesting 500 error:")
data = fetch_data("https://httpbin.org/status/500")
print(f"Received data: {data}")
Exécutez le script pour voir comment raise_for_status() gère les erreurs :
python raise_for_status_example.py
La sortie attendue montrera que la bibliothèque lève automatiquement des exceptions pour les codes d'état autres que 2xx, que votre code peut ensuite intercepter et gérer de manière appropriée.
Création de gestionnaires d'exceptions personnalisés
Pour les applications plus complexes, vous souhaiterez peut-être créer des gestionnaires d'exceptions personnalisés pour fournir une gestion des erreurs plus spécifique. Créons un exemple plus avancé :
Créez un nouveau fichier appelé custom_exception_handler.py dans votre répertoire http_status_lab :
import requests
import logging
## Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
## Custom exceptions
class APIError(Exception):
"""Base class for API request exceptions."""
pass
class ClientError(APIError):
"""Exception raised for client errors (4xx)."""
pass
class ServerError(APIError):
"""Exception raised for server errors (5xx)."""
pass
class NotFoundError(ClientError):
"""Exception raised for 404 Not Found errors."""
pass
class UnauthorizedError(ClientError):
"""Exception raised for 401 Unauthorized errors."""
pass
def api_request(url, method="get", **kwargs):
"""Make an API request with comprehensive error handling."""
try:
## Make the request using the specified HTTP method
response = getattr(requests, method.lower())(url, **kwargs)
## Log the response status
logger.info(f"Request to {url} returned status code {response.status_code}")
## Handle different status codes
if 200 <= response.status_code < 300:
return response
elif response.status_code == 401:
raise UnauthorizedError(f"Authentication required: {response.text}")
elif response.status_code == 404:
raise NotFoundError(f"Resource not found: {url}")
elif 400 <= response.status_code < 500:
raise ClientError(f"Client error {response.status_code}: {response.text}")
elif 500 <= response.status_code < 600:
raise ServerError(f"Server error {response.status_code}: {response.text}")
else:
raise APIError(f"Unexpected status code {response.status_code}: {response.text}")
except requests.exceptions.RequestException as e:
logger.error(f"Request failed: {e}")
raise APIError(f"Request failed: {e}")
## Test the custom exception handler
def test_api_request(url):
"""Test the api_request function with error handling."""
try:
response = api_request(url)
print(f"Success! Status code: {response.status_code}")
print(f"Response: {response.text[:100]}...") ## Print first 100 characters
return response
except UnauthorizedError as e:
print(f"Authentication Error: {e}")
except NotFoundError as e:
print(f"Not Found Error: {e}")
except ClientError as e:
print(f"Client Error: {e}")
except ServerError as e:
print(f"Server Error: {e}")
except APIError as e:
print(f"API Error: {e}")
return None
## Test with different status codes
print("Testing 200 OK:")
test_api_request("https://httpbin.org/status/200")
print("\nTesting 404 Not Found:")
test_api_request("https://httpbin.org/status/404")
print("\nTesting 401 Unauthorized:")
test_api_request("https://httpbin.org/status/401")
print("\nTesting 500 Internal Server Error:")
test_api_request("https://httpbin.org/status/500")
Exécutez le script pour voir comment la gestion des exceptions personnalisées fonctionne :
python custom_exception_handler.py
Cet exemple avancé démontre plusieurs concepts importants :
- Classes d'exceptions personnalisées qui étendent la classe de base
Exception - Exceptions hiérarchiques pour différents types d'erreurs
- Journalisation détaillée pour le débogage et la surveillance
- Gestion spécifique pour différents codes d'état
Cette approche vous permet de gérer différents types d'erreurs de manière plus structurée et maintenable, ce qui est essentiel pour les applications plus volumineuses.
Construction d'un client d'API Web complet
Dans cette dernière étape, nous allons rassembler tout ce que vous avez appris pour construire un client d'API web complet avec une gestion des erreurs robuste. Nous allons créer un client pour l'API JSONPlaceholder, qui est une API REST en ligne gratuite que vous pouvez utiliser pour les tests.
Création d'un client API simple
Créons un fichier appelé api_client.py dans votre répertoire http_status_lab :
import requests
import json
import logging
## Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
class APIClient:
"""A simple API client with robust error handling."""
def __init__(self, base_url):
"""Initialize the API client with a base URL."""
self.base_url = base_url
self.session = requests.Session()
def request(self, endpoint, method="get", params=None, data=None, headers=None):
"""Make a request to the API with error handling."""
url = f"{self.base_url}{endpoint}"
## Default headers for JSON APIs
if headers is None:
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
## Convert data to JSON if it's a dictionary
json_data = None
if data and isinstance(data, dict):
json_data = data
data = None
try:
## Log the request
logger.info(f"Making {method.upper()} request to {url}")
## Make the request
response = self.session.request(
method=method,
url=url,
params=params,
data=data,
json=json_data,
headers=headers
)
## Log the response status
logger.info(f"Received response with status code {response.status_code}")
## Check for HTTP errors
response.raise_for_status()
## Parse JSON response if applicable
try:
return response.json()
except json.JSONDecodeError:
return response.text
except requests.exceptions.HTTPError as e:
status_code = e.response.status_code
error_message = f"HTTP Error: {status_code}"
try:
## Try to get more details from the response
error_data = e.response.json()
error_message = f"{error_message} - {error_data.get('message', str(error_data))}"
except (json.JSONDecodeError, AttributeError):
error_message = f"{error_message} - {e.response.text if hasattr(e, 'response') else str(e)}"
logger.error(error_message)
## Re-raise with more context
if 400 <= status_code < 500:
logger.error(f"Client error: {error_message}")
raise Exception(f"Client error (HTTP {status_code}): {error_message}")
elif 500 <= status_code < 600:
logger.error(f"Server error: {error_message}")
raise Exception(f"Server error (HTTP {status_code}): {error_message}")
else:
raise
except requests.exceptions.ConnectionError as e:
logger.error(f"Connection Error: {e}")
raise Exception(f"Connection Error: Could not connect to {url}")
except requests.exceptions.Timeout as e:
logger.error(f"Timeout Error: {e}")
raise Exception(f"Timeout Error: Request to {url} timed out")
except requests.exceptions.RequestException as e:
logger.error(f"Request Exception: {e}")
raise Exception(f"Request Error: {str(e)}")
def get(self, endpoint, params=None, headers=None):
"""Make a GET request to the API."""
return self.request(endpoint, method="get", params=params, headers=headers)
def post(self, endpoint, data=None, headers=None):
"""Make a POST request to the API."""
return self.request(endpoint, method="post", data=data, headers=headers)
def put(self, endpoint, data=None, headers=None):
"""Make a PUT request to the API."""
return self.request(endpoint, method="put", data=data, headers=headers)
def delete(self, endpoint, headers=None):
"""Make a DELETE request to the API."""
return self.request(endpoint, method="delete", headers=headers)
## Test the API client with JSONPlaceholder
def test_api_client():
"""Test the API client with JSONPlaceholder."""
client = APIClient("https://jsonplaceholder.typicode.com")
try:
## Get a list of posts
print("\nGetting posts:")
posts = client.get("/posts")
print(f"Retrieved {len(posts)} posts")
print(f"First post: {posts[0]}")
## Get a single post
print("\nGetting a single post:")
post = client.get("/posts/1")
print(f"Retrieved post: {post}")
## Get a non-existent post (404)
print("\nTrying to get a non-existent post:")
try:
client.get("/posts/999999")
except Exception as e:
print(f"Expected error: {e}")
## Create a new post
print("\nCreating a new post:")
new_post = client.post("/posts", {
"title": "My New Post",
"body": "This is the content of my new post.",
"userId": 1
})
print(f"Created post: {new_post}")
## Update a post
print("\nUpdating a post:")
updated_post = client.put("/posts/1", {
"id": 1,
"title": "Updated Title",
"body": "Updated content.",
"userId": 1
})
print(f"Updated post: {updated_post}")
## Delete a post
print("\nDeleting a post:")
delete_response = client.delete("/posts/1")
print(f"Delete response: {delete_response}")
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
test_api_client()
Exécutez le script pour voir comment notre client API complet gère différents scénarios :
python api_client.py
Test de la gestion des erreurs
Créons un fichier séparé pour tester les capacités de gestion des erreurs de notre client API. Créez un fichier appelé test_error_handling.py dans votre répertoire http_status_lab :
from api_client import APIClient
import time
def test_different_errors():
"""Test different error scenarios with our API client."""
## Test with a valid API
print("\n1. Testing with a valid API:")
valid_client = APIClient("https://jsonplaceholder.typicode.com")
try:
data = valid_client.get("/posts/1")
print(f"Success! Retrieved data: {data}")
except Exception as e:
print(f"Unexpected error: {e}")
## Test with a 404 error
print("\n2. Testing with a non-existent endpoint (404):")
try:
valid_client.get("/non_existent_endpoint")
except Exception as e:
print(f"Expected error: {e}")
## Test with an invalid host
print("\n3. Testing with an invalid host (Connection Error):")
invalid_client = APIClient("https://this-does-not-exist-123456789.com")
try:
invalid_client.get("/anything")
except Exception as e:
print(f"Expected error: {e}")
## Test with a timeout
print("\n4. Testing with a timeout:")
timeout_client = APIClient("https://httpbin.org")
try:
## httpbin.org/delay/5 will delay the response for 5 seconds
## but we'll set the timeout to 2 seconds
timeout_client.session.request = lambda **kwargs: timeout_client.session.request_original(
**{**kwargs, 'timeout': 2}
)
timeout_client.session.request_original = timeout_client.session.request
timeout_client.get("/delay/5")
except Exception as e:
print(f"Expected error: {e}")
if __name__ == "__main__":
test_different_errors()
Le script ci-dessus peut produire des erreurs en raison de la gestion du délai d'attente, car il tente de modifier une méthode au moment de l'exécution. Simplifions-le pour éviter ces problèmes :
from api_client import APIClient
def test_different_errors():
"""Test different error scenarios with our API client."""
## Test with a valid API
print("\n1. Testing with a valid API:")
valid_client = APIClient("https://jsonplaceholder.typicode.com")
try:
data = valid_client.get("/posts/1")
print(f"Success! Retrieved data: {data}")
except Exception as e:
print(f"Unexpected error: {e}")
## Test with a 404 error
print("\n2. Testing with a non-existent endpoint (404):")
try:
valid_client.get("/non_existent_endpoint")
except Exception as e:
print(f"Expected error: {e}")
## Test with a server error
print("\n3. Testing with a server error (500):")
error_client = APIClient("https://httpbin.org")
try:
error_client.get("/status/500")
except Exception as e:
print(f"Expected error: {e}")
if __name__ == "__main__":
test_different_errors()
Exécutez le script de test des erreurs :
python test_error_handling.py
Ce script démontre comment notre client API gère différents scénarios d'erreur, fournissant une base solide pour les applications réelles.
Points clés à retenir
En construisant ce client API complet, vous avez appris plusieurs techniques importantes :
- Création d'une classe de client API réutilisable
- Implémentation d'une gestion des erreurs complète
- Journalisation des requêtes et des réponses pour le débogage
- Analyse de différents formats de réponse
- Gestion de différentes méthodes HTTP (GET, POST, PUT, DELETE)
Ces compétences sont essentielles pour la création d'applications Python robustes qui interagissent avec les API web, garantissant que votre code peut gérer avec élégance divers scénarios d'erreur et fournir des messages d'erreur significatifs aux utilisateurs.
Résumé
Dans ce lab, vous avez appris à gérer les codes d'état HTTP dans les requêtes Python, en progressant des techniques de base aux techniques avancées. Vous avez maintenant les compétences pour :
- Comprendre les différentes catégories de codes d'état HTTP et leurs significations
- Mettre en œuvre une gestion des erreurs de base avec la vérification des codes d'état
- Utiliser
raise_for_status()pour une gestion des erreurs simple - Créer des classes d'exceptions personnalisées pour une gestion des erreurs plus spécifique
- Construire un client API complet avec une gestion des erreurs complète
Ces compétences sont essentielles pour développer des applications Python robustes qui interagissent avec les services web. En gérant correctement les codes d'état HTTP et les erreurs, vous pouvez créer des applications plus fiables qui offrent de meilleures expériences utilisateur et sont plus faciles à maintenir.
N'oubliez pas de toujours tenir compte de la gestion des erreurs lorsque vous travaillez avec des requêtes HTTP dans vos applications Python. C'est un aspect crucial du développement web qui distingue le code professionnel des implémentations amateurs.



