Faire le point sur l'Interaction client-serveur
Dans ce cours, nous allons explorer les interactions client-serveur dans le développement web. Nous étudierons en détail les requêtes HTTP, la construction et gestion des formulaires, ainsi que les aspects sécuritaires essentiels. Nous verrons comment créer des applications web robustes qui communiquent efficacement entre le navigateur et le serveur.
L'interaction client-serveur est le fondement du web moderne. Elle permet l'échange de données entre le navigateur (client) et le serveur, rendant possible la création d'applications web dynamiques et interactives. La maîtrise de ces concepts est cruciale pour développer des applications web sécurisées et performantes.
Méthode HTTP | Usage | Sécurité données |
---|---|---|
GET | Récupérer des données | Visible dans l'URL |
POST | Envoyer des données | Dans le corps de requête |
PUT | Créer/Modifier ressource | Dans le corps de requête |
DELETE | Supprimer ressource | Paramètres URL |
📨 Exemple de requête HTTP GET : GET /api/users?page=1 HTTP/1.1 Host: monsite.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Accept: application/json Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... 📨 Exemple de requête HTTP POST : POST /api/users HTTP/1.1 Host: monsite.com Content-Type: application/json Content-Length: 87 { "nom": "Dupont", "email": "dupont@email.com", "age": 25 }
Famille | Codes courants | Signification |
---|---|---|
2xx - Succès | 200, 201, 204 | Requête traitée avec succès |
3xx - Redirection | 301, 302, 304 | Ressource déplacée ou mise en cache |
4xx - Erreur client | 400, 401, 403, 404 | Erreur dans la requête du client |
5xx - Erreur serveur | 500, 502, 503 | Problème côté serveur |
📝 Structure d'un formulaire complet : <form action="/traitement.php" method="POST" enctype="multipart/form-data"> <!-- Champs de saisie --> <label for="nom">Nom :</label> <input type="text" id="nom" name="nom" required maxlength="50"> <label for="email">Email :</label> <input type="email" id="email" name="email" required> <label for="age">Âge :</label> <input type="number" id="age" name="age" min="18" max="120"> <!-- Upload de fichier --> <label for="photo">Photo :</label> <input type="file" id="photo" name="photo" accept="image/*"> <!-- Protection CSRF --> <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>"> <button type="submit">Envoyer</button> </form>
Critère | GET | POST |
---|---|---|
Visibilité données | URL visible | Corps de requête |
Taille limite | ~2048 caractères | Théoriquement illimitée |
Mise en cache | Oui (navigateur/proxy) | Non |
Historique | Stocké | Non stocké |
Usage recommandé | Recherche, filtres | Données sensibles, upload |
🔄 Traitement sécurisé des formulaires PHP : <?php // Vérification de la méthode if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Validation CSRF if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { die('Token CSRF invalide'); } // Nettoyage et validation des données $nom = filter_input(INPUT_POST, 'nom', FILTER_SANITIZE_STRING); $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL); $age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, ['options' => ['min_range' => 18, 'max_range' => 120]]); // Vérification des erreurs $erreurs = []; if (empty($nom)) $erreurs[] = "Le nom est requis"; if (!$email) $erreurs[] = "Email invalide"; if ($age === false) $erreurs[] = "Âge invalide"; if (empty($erreurs)) { // Traitement des données (base de données, etc.) // Utilisation de requêtes préparées pour éviter l'injection SQL $stmt = $pdo->prepare("INSERT INTO users (nom, email, age) VALUES (?, ?, ?)"); $stmt->execute([$nom, $email, $age]); // Redirection après traitement réussi header('Location: /success.php'); exit; } } ?>
Menace | Description | Protection |
---|---|---|
Injection SQL | Code SQL malveillant injecté | Requêtes préparées |
XSS | Scripts malveillants dans pages | Échappement HTML |
CSRF | Requêtes non autorisées | Tokens CSRF |
Session Hijacking | Vol de session utilisateur | HTTPS + Cookies sécurisés |
🔒 Exemple de protection CSRF : <?php // Génération du token CSRF if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } ?> <!-- Dans le formulaire --> <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>"> 🛡️ Protection contre l'injection SQL : // ❌ DANGEREUX - Vulnérable à l'injection $sql = "SELECT * FROM users WHERE email = '" . $_POST['email'] . "'"; // ✅ SÉCURISÉ - Requête préparée $stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?"); $stmt->execute([$_POST['email']]); 🔐 Échappement pour éviter XSS : // Affichage sécurisé echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
🚀 Requête AJAX moderne avec Fetch API : // Envoi de données avec fetch() async function envoyerDonnees(donnees) { try { const response = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content }, body: JSON.stringify(donnees) }); if (!response.ok) { throw new Error(`Erreur ${response.status}: ${response.statusText}`); } const resultat = await response.json(); console.log('Succès:', resultat); } catch (error) { console.error('Erreur:', error); // Gestion d'erreur côté client document.getElementById('message-erreur').textContent = 'Une erreur est survenue. Veuillez réessayer.'; } } // Utilisation const formData = { nom: 'Dupont', email: 'dupont@email.com', age: 25 }; envoyerDonnees(formData);
💡 Système d'inscription complet : <!-- HTML --> <form id="inscriptionForm" action="/inscription.php" method="POST"> <div> <label for="nom">Nom complet :</label> <input type="text" id="nom" name="nom" required minlength="2" maxlength="50"> <span class="erreur" id="erreur-nom"></span> </div> <div> <label for="email">Email :</label> <input type="email" id="email" name="email" required> <span class="erreur" id="erreur-email"></span> </div> <div> <label for="motdepasse">Mot de passe :</label> <input type="password" id="motdepasse" name="motdepasse" required minlength="8"> <span class="erreur" id="erreur-motdepasse"></span> </div> <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>"> <button type="submit">S'inscrire</button> </form> <!-- JavaScript pour validation temps réel --> <script> document.getElementById('inscriptionForm').addEventListener('submit', async function(e) { e.preventDefault(); // Validation côté client const nom = document.getElementById('nom').value.trim(); const email = document.getElementById('email').value.trim(); const motdepasse = document.getElementById('motdepasse').value; let erreurs = false; if (nom.length < 2) { document.getElementById('erreur-nom').textContent = 'Le nom doit contenir au moins 2 caractères'; erreurs = true; } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { document.getElementById('erreur-email').textContent = 'Email invalide'; erreurs = true; } if (motdepasse.length < 8) { document.getElementById('erreur-motdepasse').textContent = 'Le mot de passe doit contenir au moins 8 caractères'; erreurs = true; } if (!erreurs) { // Envoi via fetch const formData = new FormData(this); try { const response = await fetch('/inscription.php', { method: 'POST', body: formData }); const result = await response.json(); if (result.success) { window.location.href = '/connexion.php?inscription=success'; } else { // Affichage des erreurs serveur result.erreurs.forEach(erreur => { console.error(erreur); }); } } catch (error) { console.error('Erreur réseau:', error); } } }); </script>
Objectif pédagogique : Comprendre les requêtes HTTP GET et manipuler les réponses JSON.
Énoncé :
1. Créez un bouton qui déclenche une requête GET vers l'API JSONPlaceholder (https://jsonplaceholder.typicode.com/posts/1)
2. Affichez le titre du post reçu dans un élément HTML
Conseil : Utilisez fetch().then() pour gérer la réponse asynchrone.
<button id="fetchBtn">Charger un post</button>
<div id="result"></div>
document.getElementById('fetchBtn').addEventListener('click', () => {
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => {
if (!response.ok) throw new Error('Erreur réseau');
return response.json();
})
.then(data => {
document.getElementById('result').textContent = data.title;
})
.catch(error => {
console.error('Erreur:', error);
});
});
Objectif pédagogique : Maîtriser l'envoi de données via POST et la prévention du rechargement de page.
Énoncé :
1. Créez un formulaire avec champs "nom" et "email" qui envoie les données en POST à https://jsonplaceholder.typicode.com/users
2. Empêchez le rechargement de page et affichez la réponse du serveur
Astuce : Utilisez preventDefault() et FormData pour collecter les données.
<form id="userForm">
<input type="text" name="name" placeholder="Nom" required>
<input type="email" name="email" placeholder="Email" required>
<button type="submit">Envoyer</button>
</form>
<div id="response"></div>
document.getElementById('userForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const result = await response.json();
document.getElementById('response').textContent = JSON.stringify(result, null, 2);
} catch (error) {
console.error('Erreur:', error);
}
});
Objectif pédagogique : Implémenter des mesures de sécurité basiques côté client.
Énoncé :
Créez un formulaire de connexion avec :
1. Validation de l'email (format valide)
2. Validation du mot de passe (8 caractères minimum, 1 majuscule, 1 chiffre)
3. Protection contre les attaques XSS en échappant les entrées utilisateur
<form id="loginForm">
<input type="email" id="email" placeholder="Email" required>
<span id="emailError" class="error"></span>
<input type="password" id="password" placeholder="Mot de passe" required>
<span id="passwordError" class="error"></span>
<button type="submit">Connexion</button>
</form>
function escapeHtml(unsafe) {
return unsafe.replace(/[&<"'>]/g, function(m) {
return {'&': '&', '<': '<', '>': '>', '"': '"', "'": '''}[m];
});
}
document.getElementById('loginForm').addEventListener('submit', (e) => {
e.preventDefault();
let isValid = true;
// Validation email
const email = escapeHtml(document.getElementById('email').value);
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
document.getElementById('emailError').textContent = 'Email invalide';
isValid = false;
} else {
document.getElementById('emailError').textContent = '';
}
// Validation mot de passe
const password = escapeHtml(document.getElementById('password').value);
const passwordRegex = /^(?=.*[A-Z])(?=.*\d).{8,}$/;
if (!passwordRegex.test(password)) {
document.getElementById('passwordError').textContent = '8 caractères min, 1 majuscule, 1 chiffre';
isValid = false;
} else {
document.getElementById('passwordError').textContent = '';
}
if (isValid) {
// Envoyer les données au serveur
console.log('Données validées:', { email, password });
}
});
Objectif pédagogique : Gérer l'authentification JWT côté client (stockage, envoi, expiration).
Énoncé :
1. Après une "connexion" réussie, stockez le JWT reçu de manière sécurisée
2. Ajoutez le JWT aux en-têtes des requêtes suivantes
3. Implémentez une déconnexion qui supprime le token
4. Vérifiez l'expiration du token avant de l'utiliser
<div id="authSection">
<form id="loginForm">
<input type="text" id="username" placeholder="Nom d'utilisateur">
<input type="password" id="password" placeholder="Mot de passe">
<button type="submit">Se connecter</button>
</form>
<button id="logoutBtn" style="display:none;">Déconnexion</button>
<button id="fetchDataBtn" style="display:none;">Charger données protégées</button>
</div>
<div id="dataContainer"></div>
// Simuler une API d'authentification
async function fakeAuthApi(username, password) {
return new Promise(resolve => {
setTimeout(() => {
// En réalité, ceci viendrait du serveur
const token = btoa(JSON.stringify({
sub: username,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 3600 // Expire dans 1h
}));
resolve({ token });
}, 500);
});
}
// Stocker le token
function storeToken(token) {
localStorage.setItem('jwt', token);
document.getElementById('loginForm').style.display = 'none';
document.getElementById('logoutBtn').style.display = 'block';
document.getElementById('fetchDataBtn').style.display = 'block';
}
// Vérifier si le token est valide
function isTokenValid() {
const token = localStorage.getItem('jwt');
if (!token) return false;
try {
const payload = JSON.parse(atob(token.split('.')[1]));
return payload.exp > Math.floor(Date.now() / 1000);
} catch {
return false;
}
}
// Gestion de la connexion
document.getElementById('loginForm').addEventListener('submit', async (e) => {
e.preventDefault();
const { token } = await fakeAuthApi(
document.getElementById('username').value,
document.getElementById('password').value
);
storeToken(token);
});
// Gestion de la déconnexion
document.getElementById('logoutBtn').addEventListener('click', () => {
localStorage.removeItem('jwt');
document.getElementById('loginForm').style.display = 'block';
document.getElementById('logoutBtn').style.display = 'none';
document.getElementById('fetchDataBtn').style.display = 'none';
});
// Requête authentifiée
document.getElementById('fetchDataBtn').addEventListener('click', async () => {
if (!isTokenValid()) {
alert('Session expirée, veuillez vous reconnecter');
return;
}
try {
const response = await fetch('https://api.example.com/protected', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('jwt')}`
}
});
const data = await response.json();
document.getElementById('dataContainer').textContent = JSON.stringify(data);
} catch (error) {
console.error('Erreur:', error);
}
});
Objectif pédagogique : Gérer l'upload de fichiers avec suivi de progression et validation.
Énoncé :
1. Créez un formulaire d'upload acceptant uniquement les images (max 2MB)
2. Affichez la progression de l'upload
3. Prévisualisez l'image avant envoi
4. Affichez un message de succès/erreur
<form id="uploadForm">
<input type="file" id="fileInput" accept="image/*">
<button type="submit">Uploader</button>
</form>
<div id="preview" style="margin:10px 0;"></div>
<progress id="progressBar" value="0" max="100" style="width:100%; display:none;"></progress>
<div id="status"></div>
const MAX_SIZE = 2 * 1024 * 1024; // 2MB
// Prévisualisation
document.getElementById('fileInput').addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
if (!file.type.match('image.*')) {
alert('Seules les images sont autorisées');
return;
}
if (file.size > MAX_SIZE) {
alert('Fichier trop volumineux (max 2MB)');
return;
}
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('preview').innerHTML = `<img src="${e.target.result}" style="max-width:200px;">`;
};
reader.readAsDataURL(file);
});
// Upload
document.getElementById('uploadForm').addEventListener('submit', function(e) {
e.preventDefault();
const file = document.getElementById('fileInput').files[0];
if (!file) {
document.getElementById('status').textContent = 'Aucun fichier sélectionné';
return;
}
const formData = new FormData();
formData.append('file', file);
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.example.com/upload', true);
// Suivi de progression
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) * 100);
document.getElementById('progressBar').value = percent;
document.getElementById('progressBar').style.display = 'block';
}
};
xhr.onload = function() {
if (xhr.status === 200) {
document.getElementById('status').textContent = 'Upload réussi!';
} else {
document.getElementById('status').textContent = 'Erreur lors de l\'upload';
}
};
xhr.send(formData);
});
Soit la droite d'équation \( 2x + 5y - 10 = 0 \). Déterminez un vecteur normal à cette droite et vérifiez votre réponse en montrant qu'il est perpendiculaire à un vecteur directeur de la droite.
Déterminez l'équation cartésienne de la droite passant par le point \( A(3, -2) \) et admettant \( \vec{n} = (1, -4) \) comme vecteur normal.
Soit le point \( M(4, 1) \) et la droite \( (d) : x - 2y + 3 = 0 \). Déterminez les coordonnées du projeté orthogonal \( H \) de \( M \) sur la droite \( (d) \). Calculez également la distance de \( M \) à la droite.
L'équation \( x^2 + y^2 + 6x - 4y - 12 = 0 \) représente-t-elle un cercle ? Si oui, déterminez son centre et son rayon.
Un cercle passe par les points \( A(1, 0) \), \( B(0, 1) \) et \( C(-1, 0) \). Déterminez l'équation de ce cercle sous forme canonique, puis vérifiez que le point \( D(0, -1) \) appartient également à ce cercle.
Contexte architectural : Un architecte dessine les plans d'un bâtiment sur un repère orthonormé où l'unité correspond à 1 mètre. Il souhaite tracer un mur rectiligne qui suit l'équation \( 3x + 4y - 24 = 0 \). Pour des raisons de sécurité, il doit installer un système d'éclairage perpendiculaire à ce mur.
Questions :
a) Déterminez un vecteur normal au mur.
b) Si le système d'éclairage part du point \( A(4, 3) \) situé sur le mur, donnez l'équation de la droite supportant ce système d'éclairage.
c) Vérifiez que le point A appartient bien au mur.
Contexte géographique : Une station météorologique surveille une zone circulaire autour de sa position. Dans un système de coordonnées où l'unité représente 1 km, la station est située au point \( S(-2, 5) \) et sa portée de surveillance s'étend sur un rayon de 8 km.
Un avion se déplace en ligne droite selon la trajectoire d'équation \( x + 2y - 11 = 0 \). Le pilote souhaite savoir s'il va traverser la zone de surveillance et, si oui, à quelle distance minimale de la station il va passer.
Questions :
a) Écrivez l'équation du cercle représentant la zone de surveillance.
b) Calculez la distance minimale entre la station et la trajectoire de l'avion.
c) L'avion traverse-t-il la zone de surveillance ? Justifiez.
Contexte sportif : Sur un terrain de football américain représenté dans un repère orthonormé (unité : 1 yard), un joueur se trouve au point \( J(15, 25) \). Il veut lancer le ballon perpendiculairement à la ligne de touche représentée par la droite d'équation \( 2x - 3y + 18 = 0 \).
Pour analyser la stratégie, l'entraîneur veut connaître précisément où le ballon va toucher la ligne de touche, ainsi que la distance que le ballon va parcourir.
Questions :
a) Déterminez les coordonnées du point \( T \) où le ballon va toucher la ligne de touche.
b) Calculez la distance \( JT \) que le ballon va parcourir.
c) Écrivez l'équation cartésienne de la trajectoire du ballon.
d) Si le ballon doit parcourir au moins 10 yards pour être valide, ce lancer est-il réglementaire ?
Contexte technologique : Une entreprise de télécommunications installe une antenne-relais dont la portée de transmission forme un cercle parfait. Les ingénieurs ont collecté les données suivantes : l'antenne émet un signal qui atteint les points \( A(2, 1) \), \( B(-1, 4) \) et \( C(5, 2) \) situés exactement à la limite de sa portée.
L'entreprise veut installer une seconde antenne au point \( D(3, -2) \) et souhaite savoir si cette position sera couverte par la première antenne. De plus, elle veut connaître l'équation de la zone de couverture pour optimiser l'implantation du réseau.
Questions :
a) Déterminez l'équation du cercle représentant la zone de couverture de l'antenne.
b) Identifiez le centre et le rayon de cette zone de couverture.
c) Le point \( D(3, -2) \) est-il dans la zone de couverture ? Justifiez votre réponse.
d) Calculez la distance entre le centre de la zone et le point D.
Contexte industriel : Une usine chimique dispose d'un système de sécurité qui surveille les fuites de gaz toxiques. Le détecteur principal est situé au point \( P(4, 6) \) et peut détecter des fuites dans un rayon de 12 mètres. Une canalisation rectiligne de gaz suit l'équation \( 3x + 4y - 36 = 0 \).
Les ingénieurs de sécurité veulent installer un second détecteur sur cette canalisation, au point le plus proche du détecteur principal, pour maximiser la couverture. Ils veulent aussi s'assurer que cette canalisation passe bien dans la zone de surveillance du détecteur principal.
Questions :
a) Déterminez les coordonnées du point \( Q \) sur la canalisation le plus proche du détecteur principal.
b) Calculez la distance \( PQ \).
c) La canalisation passe-t-elle dans la zone de surveillance du détecteur principal ?
d) Écrivez l'équation du cercle représentant la zone de surveillance.
e) Si on place le second détecteur en Q avec un rayon de surveillance de 8 mètres, les deux zones de surveillance se chevauchent-elles ?
Explorez comment l'art et la philosophie s'entrelacent pour questionner notre perception de la réalité et de l'esthétique.
Read more.Plongez dans les débats philosophiques sur la liberté, ses implications éthiques et les défis contemporains qui l'entourent.
Read more.Découvrez les différentes approches philosophiques de la vérité et comment elles influencent notre compréhension du monde.
Read more.Abonnez-vous maintenant et recevez notre newsletter hebdomadaire avec des matériaux éducatifs, de nouveaux cours, des articles intéressants, des livres populaires et bien plus encore !