IFT3225

Partie 1 : QCM

1. B (201 Created). La ressource est créée et renvoyée immédiatement avec son id.

2. D (PUT /stations/12). On cherche une méthode idempotente : la rejouer donne le même état final.

3. C (404 Not Found). La ressource n’existe pas.

4. A. DELETE /stations/12. L’action (intention) est déterminée purement par le verbe HTTP.

5. B. POST /stations, GET /stations/12, PATCH /stations/12, DELETE /stations/12.

6. A. Le client joint à chaque requête un cookie ou un jeton que le serveur vérifie.

7. C. Le serveur vérifie la signature du jeton avec sa clé et se fie au contenu, sans rien stocker : c’est l’intérêt du JWT.

8. D (côté client). Une préférence d’affichage est locale et non critique : un cookie ou le stockage du navigateur suffit.

9. B. req.params.id lit le segment de route (12), req.query.detail lit la chaîne de requête (complet).

10. A. Sans app.use(express.json()), le corps JSON n’est pas analysé et req.body reste undefined, même avec le bon Content-Type.


Partie 2 : Questions à développement

Q1. Maîtrise et limites de HTTP

La caractéristique en jeu est que HTTP est sans état : chaque requête est traitée indépendamment, sans mémoire automatique des requêtes précédentes.

La limite concrète est donc que HTTP ne permet pas, à lui seul, de savoir qu’un utilisateur avait déjà ajouté des articles à son panier lors d’une requête précédente.

Il est possible de gérer un panier d’achats en ajoutant une couche applicative au-dessus de HTTP. Par exemple, on peut utiliser un cookie contenant un identifiant de session, une session côté serveur, un jeton d’authentification ou encore une base de données.

Q2. L’architecture REST (C3).

Une API qui « parle HTTP » utilise HTTP comme simple canal de transport, mais n’exploite pas vraiment la sémantique de HTTP.
Une API RESTful représente plutôt les données comme des ressources identifiables par des URI, par exemple GET /users/12.

Trois contraintes REST importantes sont :

  1. Interface uniforme
  1. Sans état
  1. Mise en cache

La première API perd donc une partie des bénéfices de HTTP : lisibilité, usage correct des méthodes, cache, interopérabilité, observabilité et prévisibilité. Elle utilise HTTP, mais ne respecte pas vraiment l’esprit REST. La seconde API est plus RESTful parce qu’elle expose des ressources et utilise la méthode HTTP pour exprimer l’action attendue.


Partie 3 : analyse et réparation

Q1. Réparer une conception d’endpoints

Problèmes (au moins cinq) :

  1. GET /getBoites : le verbe est dans le chemin.
  2. GET /boite?id=42 : la ressource passe par un paramètre de requête au lieu du chemin, et le nom est au singulier.
  3. POST /boites/42/modifier : action dans le chemin.
  4. GET /boites/supprimer/42 : suppression par GET, ce qui viole la sémantique de GET.
  5. POST /boites renvoie 200 au lieu de 201 Created pour une création.
  6. GET /boites/9999 (inexistante) renvoie 200 {} au lieu de 404 Not Found.

Version corrigée :

Méthode Chemin Succès Erreur pertinente
GET /boites 200
GET /boites/42 200 404
PATCH /boites/42 200 404, 400
DELETE /boites/42 204 404
POST /boites 201 400
GET /boites/9999 404

Deux justifications :

Q2. Analyser et réparer un protocole de collecte

1. L’enregistrement se fait par GET /enregistrer?..., donc une écriture par un GET : violation de la sémantique de GET.
Le serveur répond toujours 200 "ok", même en cas de champs manquants ou invalides : aucune validation, aucun code d’erreur.
La lecture GET /mesure/12 renvoie 200 {} pour une station inconnue au lieu de 404.

2. Interroger 500 stations toutes les 2 secondes, même sans aucune nouveauté : gaspillage de bande passante et de charge serveur, et fraîcheur toujours en retard.
Pour un besoin de mise à jour en temps réel, il serait préférable d’utiliser un mécanisme où le serveur pousse les nouveautés vers le client, plutôt que d’attendre que le client les demande en boucle.
C’est le principe du push serveur, par exemple avec WebSocket ou Server-Sent Events. Ainsi, le serveur transmet seulement les nouvelles mesures lorsqu’elles arrivent.

3. Au point (a), Mesure.create(...) écrit dans la base de données de façon asynchrone : sans await, le serveur répond avant la fin de l’écriture et n’attrape aucune erreur. Il faut valider les champs avant d’écrire (refuser par 400 si une donnée manque ou est invalide), puis await la création dans un try/catch.

4. Version corrigée

// Enregistrer une mesure : POST (écriture), validation, 201, 400, 404
router.post("/stations/:id/mesures", async (req, res) => {
  const station = await Station.findById(req.params.id);
  if (!station) {
    return res.status(404).json({ erreur: "station inconnue" });
  }

  const { pm25, temp } = req.body ?? {};
  if (pm25 == null || temp == null) {
    return res.status(400).json({ erreur: "champs manquants ou invalides" });
  }

  try {
    const mesure = await Mesure.create({ station: station.id, pm25, temp }); 
    res.status(201).json(mesure);                                            
  } catch (e) {
    res.status(400).json({ erreur: "mesure invalide" });
  }
});

Partie 4 : problème de conception d’API (Atmo)

Solution de référence. D’autres découpages sont acceptables s’ils sont cohérents et justifiés.

(a) Ressources et attributs

Ressource Attributs clés (type) Relations
Station id (identifiant), nom (texte), quartier (texte), position ({lat, lon} nombres), statut (active/inactive) possède des capteurs, produit des mesures
Capteur id, type (pm25, temperature, humidite, bruit), unite (texte) appartient à une station
Mesure id, date (horodatage ISO 8601), valeurs (pm25, temperature, humidite, bruit : nombres), indice (texte dérivé) rattachée à une station

(b) Endpoints

Méthode et chemin Renvoie
GET /stations la liste des stations (id, nom, quartier, position)
GET /stations?quartier=Rosemont les stations d’un quartier (filtre par paramètre de requête)
GET /stations/:id le détail d’une station
GET /stations/:id/mesures?sort=timestamp:desc&limit=1
OU GET /stations/:id/mesures/latest (acceptable)
la dernière mesure de la station
GET /stations/:id/mesures?debut=...&fin=... l’historique des mesures sur une période
GET /stations?quartier=X&sort=qualityIndex:desc&limit=1 la station du quartier à la pire qualité de l’air

(c) Stratégie de persistance

Les stations (peu nombreuses, lues souvent) et les mesures (volume croissant sans fin) vivent en base de données, pas en mémoire : il faut durabilité, partage entre serveurs et interrogation.
Modèle : une collection stations, une collection mesures référençant la station et portant un date, avec un index sur { station, date }.