Chapitre 4.4 : Gestion des erreurs
Temps d'étude : 45 minutes
1. Gestion des erreurs : protocoles d'urgence du MCC
Dans l'espace, tout peut mal tourner : une éruption solaire peut interrompre les communications, l'ordinateur de bord du vaisseau peut tomber en panne, et une commande venant de la Terre peut contenir des coordonnées incorrectes.
La gestion des erreurs sur le frontend est l'équivalent des protocoles d'urgence de votre MCC. Ils doivent :
- 🚨 Empêcher l'interface entière de "planter" à cause d'une seule commande défectueuse.
- 📡 Informer clairement l'opérateur (utilisateur) de ce qui n'a pas fonctionné.
- 🔧 Proposer des actions ultérieures possibles.
💡 Analogie spatiale :
Si un signal
500 Internal Server Error
est reçu du vaisseau, l'écran du MCC ne doit pas afficher "Erreur JavaScript critique à la ligne 57". Au lieu de cela, il devrait y avoir : "🚨 Défaillance à bord du vaisseau ! Les ingénieurs ont déjà été notifiés. Veuillez réessayer la commande ultérieurement."
2. Types d'"anomalies spatiales"
Sur le frontend, nous rencontrons trois types principaux d'erreurs lors de l'interaction avec une API :
- Erreurs réseau : La connexion au serveur n'a pas été établie. L'antenne ne fonctionne pas, le câble est coupé.
fetch
"tombera" dans le bloc.catch()
. - Erreurs client (4xx) : La commande venant de la Terre était incorrecte. ID invalide, erreur de validation. Le serveur répond, mais avec un statut
4xx
. - Erreurs serveur (5xx) : Défaillance sur le vaisseau lui-même. Problème dans le code de l'API. Le serveur répond, mais avec un statut
500+
.
Nous avons déjà commencé à les gérer avec try...catch
et la vérification de response.ok
. Faisons maintenant cela de manière centralisée.
3. Fonction de gestion centralisée
Répéter le même code try...catch
dans chaque fonction est une mauvaise pratique. Créons un "wrapper" universel pour nos requêtes fetch
.
Étape 1 : Créer api.js
Créez un nouveau fichier api.js
à côté de app.js
. Nous y déplacerons toute la logique d'interaction avec l'API.
// api.js
const API_BASE_URL = 'http://127.0.0.1:8000';
/**
* Fonction universelle pour exécuter des requêtes vers l'API.
* Gère les erreurs et retourne du JSON.
* @param {string} endpoint - Point d'accès de l'API, par exemple, '/spaceships'
* @param {object} options - Paramètres pour fetch (method, headers, body)
*/
async function apiRequest(endpoint, options = {}) {
const url = `${API_BASE_URL}${endpoint}`;
try {
const response = await fetch(url, options);
// Si la réponse n'est pas du tout du JSON, nous levons immédiatement une erreur
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
// Exception pour une requête DELETE réussie qui n'a pas de corps
if (response.status === 204) return null;
throw new TypeError(`Réponse non-JSON reçue du serveur : ${response.statusText}`);
}
const data = await response.json();
if (!response.ok) {
// Si le serveur a renvoyé du JSON avec une erreur (par exemple, detail de FastAPI)
const errorMessage = data.detail || `Erreur HTTP ! Statut : ${response.status}`;
throw new Error(errorMessage);
}
return data;
} catch (error) {
console.error(`Erreur de requête API vers ${endpoint} :`, error);
// Nous "relançons" l'erreur pour qu'elle puisse être interceptée dans l'UI
throw error;
}
}
Étape 2 : Inclure api.js
dans index.html
Il est important de l'inclure AVANT app.js
, car app.js
utilisera ses fonctions.
<!-- index.html -->
<body>
<!-- ... -->
<script src="api.js"></script>
<script src="app.js"></script>
</body>
Étape 3 : Refactoriser app.js
Nous allons maintenant réécrire nos fonctions en utilisant le nouveau apiRequest
.
// app.js
// const API_BASE_URL = ...; // Cette ligne peut être supprimée, elle est maintenant dans api.js
// ...
async function fetchAndDisplayFleet() {
try {
fleetList.innerHTML = '<li>Chargement de la télémétrie...</li>';
const ships = await apiRequest('/spaceships'); // <-- Nous utilisons notre wrapper !
fleetList.innerHTML = '';
if (ships.length === 0) {
fleetList.innerHTML = '<li>Aucun appareil n\'est enregistré dans le registre.</li>';
return;
}
ships.forEach(ship => { /* ... le reste du code d'affichage ... */ });
} catch (error) {
fleetList.innerHTML = `<li>🔴 Erreur de chargement de la flotte : ${error.message}</li>`;
}
}
async function createShip(event) {
event.preventDefault();
const shipData = { /* ... collecte des données du formulaire ... */ };
try {
createStatusMessage.textContent = 'Envoi de la commande de lancement...';
const options = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(shipData)
};
const newShip = await apiRequest('/spaceships', options); // <-- Nous utilisons notre wrapper !
createStatusMessage.textContent = `🚀 Lancement réussi ! L'appareil a l'ID : ${newShip.id}`;
createShipForm.reset();
fetchAndDisplayFleet();
} catch (error) {
createStatusMessage.textContent = `🔴 Erreur : ${error.message}`;
}
}
// Réécrivez les autres fonctions (fetchShipById, deleteShip) de la même manière !
response.ok
et d'analyse du JSON se trouve au même endroit, et le code dans app.js
est devenu beaucoup plus propre et lisible.
4. Affichage des erreurs à l'utilisateur
Une bonne interface ne doit pas se contenter d'écrire l'erreur dans la console, mais la montrer à l'utilisateur sous une forme compréhensible.
Exemple : Amélioration de createShip
Notre code le fait déjà : createStatusMessage.textContent = ...
. Mais on peut faire encore mieux en créant une fonction universelle pour afficher les notifications.
Ajouter à app.js
:
// app.js
function showNotification(message, isError = false) {
const notificationArea = document.getElementById('create-status-message'); // ou un autre élément
notificationArea.textContent = message;
notificationArea.style.color = isError ? 'red' : 'green';
}
// Utilisation dans createShip :
async function createShip(event) {
// ...
try {
// ...
const newShip = await apiRequest('/spaceships', options);
showNotification(`🚀 Lancement réussi ! ID : ${newShip.id}`);
// ...
} catch (error) {
showNotification(`🔴 Erreur : ${error.message}`, true);
}
}
Quiz de consolidation
🚀 Résumé du chapitre :
Vous avez renforcé votre MCC en créant des protocoles d'urgence fiables.
- 🛡️ Vous comprenez la différence entre les erreurs réseau, client et serveur.
- ⚙️ Vous avez créé une fonction
apiRequest
centralisée pour gérer toutes les requêtes, évitant ainsi la duplication de code. - 📡 Votre interface est désormais capable d'informer correctement l'utilisateur des erreurs, la rendant plus conviviale et fiable.
Boucliers d'urgence levés ! Mais qu'est-ce qui est mieux : les chaînes
.then()
ou leasync/await
moderne ? Dans le chapitre suivant, nous analyserons les deux approches et comprendrons quand utiliser laquelle.
📌 Vérification :
- Vérifiez que votre code dans
app.js
est correctement refactorisé et utilise la nouvelle fonctionapiRequest
.- Essayez d'arrêter le serveur FastAPI et de cliquer sur le bouton "Demander des données". Vous devriez voir une erreur de connexion sur la page.
- Essayez de créer un vaisseau avec des données non valides. Vous devriez voir un message d'erreur de validation provenant de FastAPI.
⚠️ En cas d'erreurs :
apiRequest is not defined
: Assurez-vous d'avoir connectéapi.js
dansindex.html
avantapp.js
.- Vérifiez la console du navigateur pour d'autres erreurs de syntaxe en JavaScript.