Ce projet consiste à réaliser un service simple de réduction d’URL comme https://bit.ly/ ou https://tinyurl.com/ Il s’agit d’abord de la réalisation d’une API serveur REST puis d’un client :
reponses
.api-v1
.api-v2
.client-ajax
.api-v2-delete
.Un projet d’application est disponible sur Github https://github.com.
Le service à réaliser permet de transformer une URL longue, comme https://nodejs.org/api/http.html#event-connect_1 en une URL courte http://localhost/WmFJQp qui redirige vers l’URL d’origine quand on se rend dessus.
Le service propose les routes suivantes, qui répondent des documents JSON (en-tête Content-Type: application/json
) :
GET /
renvoie le nombre de liens déjà créés;POST /
: créée un lien réduit à partir d’une URL longue. Vérifie que l’URL soit syntaxiquement valideGET /status/:url
: donne l’état du lien : date de création, origine et nombre de visitesLa route suivante en revanche, ne répond pas de JSON mais redirige le navigateur :
GET /:url
: redirige le lien raccourci vers le lien donné lors de la créationCréer votre dépot sur GitHub puis cloner votre projet.
Vous disposez d’un projet de départ fonctionnel où les routes GET /
et POST /
de l’API V1 sont déjà implémentées.
Une route GET /error
qui génère une erreur 500 est aussi créée pour les tests.
En revanche, les routes GET /:url
et GET /status/:url
ne sont pas implémentées et renvoient pour l’instant une erreur 501.
Avant de démarrer, créer un fichier nommé .env
à la racine du projet avec le contenu comme suit, à adapter à votre environnement si besoin.
Ce fichier .env
est automatiquement chargé par l’application pour définir les variables d’environnement nécessaires à l’exécution de l’application.
PORT=8080
LINK_LEN=6
DB_FILE=database/database.sqlite
DB_SCHEMA=database/database.sql
npm run dev
, vous travaillerez essentiellement dans ce mode,npm run prod
.La base de données database/database.sqlite
est re-créée, si nécesaire, automatiquement par l’application.
Vous pouvez y accéder avec votre interface SQLite favorite comme https://sqlite.org (utiliser une version récente).
Après avoir installé puis lancé l’application, consultez http://localhost:8080/api-docs qui donne une documentation Swagger UI interactive pour vérifier que tout fonctionne.
Pour les tests manuels, sans utiliser Swagger, installez https://httpie.io/ (recommandé) ou https://curl.se/ (le plus connu, mais moins facile) et exécutez une des commandes suivantes selon le logiciel installé :
## exemple de requête GET
# pour curl
curl --include --header 'Accept: application/json' --request GET http://localhost:8080/api-v1/
# pour httpie
http http://localhost:8080/api-v1/
## exemples de requêtes POST
# pour curl
curl --include --header 'Accept: application/json' --header 'Content-Type: application/json' --request POST http://localhost:8080/api-v1/ --data '{"url": "https://perdu.com"}'
# pour httpie
http POST http://localhost:8080/api-v1/ url="https://perdu.com"
Une documentation complète est fournie sur le chemin http://localhost:8080/api-docs/. Cette documentation est interactive et vous permet d’envoyer des requêtes comme les précédentes via son interface, ceci permet de tester les différentes routes déjà implémentées et celles à réaliser ensuite.
La documentation est entièrement générée à partir du d’une spécification écrite au format YAML dans le fichier static/open-api.yaml
.
Le middleware Swagger UI Express permet de faire le rendu.
NOTA BENE la page http://localhost:8080/api-docs/ est très importante pour tout le déroulement du projet car elle vous permet de tester vos fonctionnalités.
Le projet est à rendre sur Github https://github.com et sur Render https://render.com
À chaque partie complétée, il faut associer un tag GIT.
Penser également à compléter le fichier README.md
avec votre état d’avancement.
README.md
du projet de départasync/await
et codes retours HTTP associésREADME.md
L’évaluation des fonctionnalités sera en partie automatisée, il faut donc respecter srupuleusement les schéma des réponses JSON demandés.
NOTA BENE Pour ajouter un tag, utiliser la fonctionnalité de VSCode, l’interface Web de GitHub ou la commande git
comme git tag mon-tag main
et git push origin mon-tag
.
Dans tous les cas pensez à pousser les tags sur le dépôt GitHub, les tags ne sont transmis qu’avec un push spécifique.
Répondre aux questions précédentes dans le fichier REPONSES.md
de votre dépôt.
httpie
correspondant à la commande curl
donnée par la doc pour la route POST
.npm run prod
puis en mode développement avec npm run dev
. Donner les principales différences entre les deux modes.npm
qui permet de formatter automatiquement tous les fichiers .mjs
X-Powered-By
. Donner la configuration Express à modifier pour qu’elle n’apparaisse plus.middleware
(niveau application) qui ajoute un header X-API-version
avec la version de l’application. Donner le code.favicon.ico
avec static/logo_univ_16.png
. Donner le code.Ctrl+Shift+R
. Conlure sur la gestion du cache par Express.npm run dev
et une autre sur le port 8081 avec la commande cross-env PORT=8081 NODE_ENV=development npx nodemon server.mjs
. Créer un lien sur la première instance http://localhost:8080/ et ensuite un autre sur la seconde instante http://localhost:8081/. Les liens de l’un doivent être visibles avec l’autre. Expliquer pourquoi.database/database.mjs
, indiquer quelle fonction l’utilise dans le code du routeur./error
fait planter le serveur au lieu de provoquer une erreur 500 attendue. Trouver quel est le handler qui fait crasher le serveur et expliquer pourquoi.Pousser le fichier REPONSES.md
dans votre dépôt pour le jeudi 1er septembre 23h59.
Mettre le tag reponses
à la version correspondant dans votre dépôt Git.
Dans cette partie, il n’y a pas encore de front-end, il s’agit uniquement d’un back-end qui va recevoir des requêtes HTTP GET
ou POST
et répondre des contenus JSON ou rediriger sans aucune CSS ni HTML.
router/api-v1.mjs
et database/database.mjs
.visit
de la base de données à chaque visite sur GET /:url
.config.mjs
qui sera chargé par les autres.NOTA BENE il faut respecter les spécifications fournies pour ces fonctionnalités, dont notamment la structure des objets JSON des réponses.
Réaliser ces fonctionnalités et les pousser dans le dépôt.
Mettre le tag api-v1
à la version correspondant dans votre dépôt Git.
Dans cette partie on va adapter le type de la réponse à la demande du client, c’est-à-dire fournir une réponse JSON comme précédement ou une réponse HTML (générée côté serveur) selon ce que l’utilisateur souhaite via l’en-tête de requête Accept
.
La méthode res.format() de la classe Response
d’Express permet de faire de la négotiation de contenu, c’est-à-dire, d’adapter le format de la réponse (en-tête de réponse Content-Type
) à ce que l’utilisateur accepte (en-tête de requête Accept
).
Ainsi, réorganisez les routes de l’application pour que chacune réponse soit fournie soit en application/json
soit en text/html
selon ce que l’utilisateur a démandé :
GET /
:
json
: renvoie le nombre de liens sur le serveurhtml
: renvoie une page d’accueil avec création de lienPOST /
:
json
: renvoie les informations sur le lien crééhtml
: renvoie une page avec le lien crééeGET /:url
:
json
: envoie les informations sur le lien, comme le faisait la route GET /status/:url
html
: incrémente le compteur de visites et redirige le lien raccourciGET /status/:url
GET /:url
en JSONEn cas d’un autre format que JSON ou HTML, retourner une erreur HTTP 406 Not Acceptable.
Pour cela créer un nouveau routeur que vous brancherez sur la route api-v2
, en vous inspirant de ce qui est fait pour la version 1 mais en ajoutant la négociation de contenus.
Faire une première version HTML basique, puis utiliser des templates https://ejs.co/ pour faire le rendu avec la méthode response.render()
et réemployant le template views/root.ejs
existant.
A titre d’exemple, voici le rendu des différentes pages :
Une base de départ du formulaire HTML est la suivante :
<form method="post" action="/api-v2/" id="submit-link">
<label for="url">Type the URL to shorten</label>
<input name="url" id="url" type="url" placeholder="https://perdu.com" />
<button type="submit">Submit</button>
</form>
Réaliser ces fonctionnalités et les pousser dans le dépôt.
Mettre le tag api-v2
à la version correspondant dans votre dépôt Git.
NOTA BENE pensez à choisir le bon serveur pour l’API v2 en haut dans l’interface de Swagger, comme dans capture d’écran ci-dessous.
Il s’agit maintenant de créer un client de type Single Page Application.
Cette fois-ci, le HTML static/client.html
et le fichier static/app.js
seront directement servis statiquement par Express, sans utilisation de moteur template.
Le client, reprendra l’interface précédente mais cette fois-ci, tous les échanges entre le navigateur et le serveur se feront en JSON sur l’API v2 via des appels fetch
.
Tout se déroulera dans une page unique index.html
qui contiend un formulaire de saisie d’URL et affiche le lien raccourci ou une erreur le cas échéant.
Quand on soumet le forumaire, c’est une requête POST /
qui est émise dont le retour JSON sert à mettre à jour la page.
On ne demande pas d’implémenter d’équivalent de GET /status/:url
. Pour la redirection, il n’y a rien à faire de plus que l’API v2.
Pour faciliter l’utilisation, quand un lien est généré avec succès, on proposera un bouton Copier l’URL qui copie le lien dans le presse-papier grâce à la Clipboard.writeText() de la Clipboard API cmme dans l’image ci-dessous :
NOTA BENE La page index.html
pourrait être servie par un autre serveur que celui de l’API, par exemple avec python -m http.server
. Si vous le tester, il faudra modifier la première ligne de static/app.js
par const originURL = "http://127.0.0.1:8080/";
avec votre serveur Node.js.
Réaliser ces fonctionnalités et les pousser dans le dépôt.
Mettre le tag client-ajax
à la version correspondant dans votre dépôt Git.
Maintenant, on souhaite ajouter une fonctionnalité de suppression de lien raccourci.
Pour cela, on créé une route DELETE /api-v2/:url
qui supprime le lien, mais on veut contrôler que seul l’auteur puisse supprimer en lui donnant un code secret généré lors de la création du lien.
Ce secret devra être fourni lors de la requête de suppression dans l’en-tête X-API-Key
pour que celle-ci réussisse.
Tout d’abord, il faut ajouter cette fonctionnalité dans la documentation en complétant le fichier static/open-api.yaml
:
Ensuite :
secret
database/database.sqlite
pour qu’elle soit reconstruite par l’application;DELETE /api-v2/:url
avec le comportement suivant :
X-API-Key
, retourner un code HTTP 401;X-API-Key
mais que le secret ne correspond pas au lien demandé, retourner un code HTTP 403;Tester que la documentation interactive fonctionne bien.
Si la spécification est bonne, vous aurez un champ de saisie avec un petit cadenas dans l’interface Swagger comme suit pour saisir l’en-tête X-API-KEY
:
Vous pouvez aussi tester avec les commandes suivantes, par exemple pour supprimer le lien HdPWk7
avec la clef RatQak
:
# pour curl
curl --include --header 'Accept: application/json' --header 'X-API-KEY: RatQak' --request DELETE http://localhost:8080/api-v2/HdPWk7
# pour httpie
http DELETE http://localhost:8080/api-v2/HdPWk7 X-API-KEY:RatQak
NOTA BENE faire attention à ne pas révéler le secret sur la route GET /:url
en JSON.
NOTA BENE on de demande pas de front pour cette fonctionnalité, juste la mise-à-jour de l’API v2 et de la documentation OpenAPI.
Réaliser ces fonctionnalités et les pousser dans le dépôt.
Mettre le tag api-v2-delete
à la version correspondant dans votre dépôt Git.