
Gestion des requêtes et des réponses (request, response)
Maîtrisez la manipulation des objets `request` (IncomingMessage) et `response` (ServerResponse) du module `http` de Node.js pour traiter les requetes et construire les reponses HTTP.
Les acteurs principaux de la transaction HTTP
Au coeur de chaque interaction gérée par un serveur `http.createServer` se trouvent deux objets fondamentaux passés à votre fonction de rappel (request listener) : l'objet `request` (souvent abrégé en `req`) et l'objet `response` (souvent abrégé en `res`). Comprendre comment lire les informations de la requête entrante et comment utiliser l'objet réponse pour construire et envoyer la réponse sortante est la compétence de base pour développer des serveurs HTTP en Node.js.
Ces objets ne sont pas de simples conteneurs de données ; ils sont également des instances de Streams Node.js, ce qui leur confère des capacités de traitement asynchrone des données, particulièrement importantes pour gérer les corps de requêtes et de réponses potentiellement volumineux.
Explorer l'objet `request` (`req` - `http.IncomingMessage`)
L'objet `request` encapsule toutes les informations envoyées par le client au serveur dans le cadre de la requête HTTP. C'est un Readable Stream, ce qui signifie que vous pouvez lire les données du corps de la requête (si présentes) de manière asynchrone.
Voici ses propriétés et méthodes les plus importantes :
- `req.url` (String) : Contient le chemin de l'URL demandé par le client, y compris la chaîne de requête (query string). Par exemple :
'/','/users/123','/search?q=node'. C'est l'information la plus utilisée pour le routage. - `req.method` (String) : Indique la méthode HTTP utilisée pour la requête. Les valeurs courantes sont
'GET','POST','PUT','DELETE','PATCH','OPTIONS','HEAD'. Essentiel pour déterminer l'intention du client. - `req.headers` (Object) : Un objet JavaScript contenant tous les en-têtes HTTP envoyés par le client. Les noms des en-têtes sont normalisés en minuscules. Exemples d'accès :
req.headers['content-type'],req.headers['user-agent'],req.headers['authorization']. - `req.httpVersion` (String) : La version du protocole HTTP utilisée par le client (ex: `'1.1'`, `'2.0'`).
Lecture du corps de la requête (pour POST, PUT, etc.) : Puisque `req` est un Readable Stream, la lecture du corps (payload) se fait en écoutant les événements `data` et `end`.
// ... dans http.createServer((req, res) => { ...
if (req.method === 'POST' && req.url === '/submit-data') {
let body = '';
// Ecouter les morceaux de données entrants (Buffers)
req.on('data', (chunk) => {
body += chunk.toString(); // Convertir le Buffer en chaîne et l'accumuler
});
// Evénement déclenché quand tout le corps a été reçu
req.on('end', () => {
console.log('Corps de la requête reçu :');
console.log(body);
// Maintenant, vous pouvez traiter le 'body'
// Par exemple, le parser s'il est en JSON :
try {
const parsedData = JSON.parse(body);
console.log('Données parsées:', parsedData);
// Faire quelque chose avec les données...
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', received: parsedData }));
} catch (e) {
res.writeHead(400, { 'Content-Type': 'application/json' }); // 400 Bad Request
res.end(JSON.stringify({ status: 'error', message: 'Invalid JSON format' }));
}
});
req.on('error', (err) => {
console.error('Erreur lors de la lecture du corps de la requête:', err);
res.statusCode = 500;
res.end('Erreur serveur lors de la lecture de la requête.');
});
} else {
// Gérer les autres méthodes/URL...
res.statusCode = 404;
res.end('Not Found');
}
// ... fin du listener ...
Il est crucial de gérer l'événement `end` pour savoir quand vous avez reçu l'intégralité du corps avant de tenter de le traiter.
Construire la reponse avec l'objet `response` (`res` - `http.ServerResponse`)
L'objet `response` est votre outil pour construire la réponse HTTP qui sera renvoyée au client. C'est un Writable Stream, vous permettant d'écrire le corps de la réponse, potentiellement en plusieurs fois.
Les méthodes et propriétés clés pour façonner la réponse :
- `res.statusCode` (Number) : Permet de définir le code de statut HTTP (par défaut 200). Doit être défini avant l'envoi des en-têtes (avant `writeHead` ou la première écriture).
- `res.statusMessage` (String) : Permet de définir le message de statut textuel (par défaut associé au `statusCode`, ex: 'OK', 'Not Found'). Rarement modifié manuellement.
- `res.setHeader(name, value)` : Définit la valeur d'un en-tête HTTP spécifique. Si l'en-tête existe déjà, il est remplacé. Pour définir plusieurs en-têtes avec le même nom (comme `Set-Cookie`), `value` peut être un tableau de chaînes.
- `res.getHeader(name)` : Récupère la valeur d'un en-tête qui a été défini (mais pas encore envoyé).
- `res.removeHeader(name)` : Supprime un en-tête qui a été défini.
- `res.headersSent` (Boolean) : Indique si les en-têtes ont déjà été envoyés au client (après `writeHead` ou la première utilisation de `write`/`end`). Utile pour éviter de modifier les en-têtes ou le statut après leur envoi.
- `res.writeHead(statusCode, [statusMessage], [headers])` : Envoie explicitement la ligne de statut et les en-têtes au client. L'argument `headers` est un objet où les clés sont les noms des en-têtes. Cette méthode ne doit être appelée qu'une seule fois par réponse et avant `res.write` ou `res.end`. Si elle n'est pas appelée explicitement, les en-têtes sont envoyés implicitement lors du premier appel à `res.write` ou `res.end`.
- `res.write(chunk, [encoding], [callback])` : Ecrit un morceau (`chunk`, qui peut être une chaîne ou un Buffer) dans le corps de la réponse. Peut être appelée plusieurs fois pour envoyer des données en streaming.
- `res.end([data], [encoding], [callback])` : Finalise la réponse. Elle envoie éventuellement un dernier morceau de données (`data`), puis signale au serveur que la réponse est complète. Il est impératif d'appeler `res.end()` pour chaque réponse, sinon le client attendra indéfiniment.
Exemple de construction de réponse :
// ... dans http.createServer((req, res) => { ...
if (req.url === '/status') {
// Exemple d'utilisation de writeHead
res.writeHead(200, { // Code 200 OK + Headers
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
});
const statusInfo = { status: 'running', uptime: process.uptime() };
res.end(JSON.stringify(statusInfo)); // Envoyer le corps JSON et terminer
} else if (req.url === '/stream-data') {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
// Envoyer en plusieurs morceaux
res.write('Début du flux...\n');
setTimeout(() => {
res.write('Données intermédiaires...\n');
setTimeout(() => {
res.end('...Fin du flux.\n'); // Terminer la réponse
}, 1000);
}, 1000);
} else {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Inconnu');
}
// ... fin du listener ...
L'importance de terminer la reponse (`res.end()`)
Il est crucial de répéter l'importance d'appeler `res.end()` pour chaque cycle de requête/réponse. C'est cette méthode qui signale la fin de la réponse au client et permet au serveur de clore proprement la connexion (ou de la réutiliser pour HTTP/1.1 Keep-Alive). Si vous oubliez `res.end()`, le client restera en attente, consommant des ressources côté client et côté serveur, et ne recevra jamais la confirmation que la réponse est complète.
Maîtriser la lecture des informations pertinentes de l'objet `request` et l'utilisation adéquate des méthodes de l'objet `response` (en particulier `statusCode`, `setHeader`, `write`, et surtout `end`) constitue le fondement de la création de serveurs HTTP fonctionnels et corrects avec le module `http` de Node.js.