Routes
Le serveur "attend la commande du client" et va répondre à sa demande. La création d'un serveur consiste donc à définir les différentes commandes que le serveur va pouvoir honorer, et la manière de le faire.
On peut voir le serveur comme un "restaurant", où le client (le navigateur) va passer une commande (une requête HTTP) et le serveur transmettre la commande à la cuisine (le code python) pour la préparer. Le serveur va ensuite transmettre la réponse au client (le plat préparé).
La demande est faite par le client par l'envoi d'une requête HTTP. L'URL de la requête, la méthode HTTP utilisée (GET, POST, etc.), et les données envoyées (le cas échéant) sont des éléments clés pour déterminer comment le serveur va répondre.
Notion de route
Définir des routes permet d'associer un ou plusieurs méthodes et une ou plusieurs URLs à une fonction python. Le framework va alors utiliser cette fonction pour créer la réponse HTTP à envoyer au client.
Bottle (comme la plupart des autres frameworks) utilise un décorateur pour
définir une route. Un décorateur est une fonction qui va modifier le
comportement d’une autre fonction. En l’occurrence ici, ce décorateur va ajouter
à la fonction hello la capacité à répondre à une requète http précise (lire
les en-têtes et les données de la requète, rajouter les codes / en-têtes de la
réponse, etc...).
Le décorateur @route utilisé ici effectue la liaison entre une URL et une
fonction python, et enregistre cette association dans le routeur global de
l'application.
@route('/hello')
def hello():
return "Hello World!"
La fonction hello() est appelée lorsque le serveur reçoit une requête sur
cette URL dont le chemin est exactement /hello et la méthode GET. La
fonction doit retourner une chaîne de caractères qui sera envoyée au client dans
la réponse HTTP.
Il est possible d'associer plusieurs routes à la même fonction, en spécifiant
plusieurs URL dans le décorateur @route :
@route('/hello')
@route('/salut')
def hello():
return "Hello World!"
Il est aussi possible de spécifier plusieurs méthodes HTTP pour une même route
en précisant la liste des méthodes dans le décorateur @route :
@route('/hello', method=['GET', 'POST'])
def hello():
return "Hello World!"
Des raccourcis existent :
@get('/hello'): équivalent à@route('/hello', method=['GET'])@post('/hello'): équivalent à@route('/hello', method=['POST'])@put('/hello'): équivalent à@route('/hello', method=['PUT'])@delete('/hello'): équivalent à@route('/hello', method=['DELETE'])- ...
Il faut penser à les importer avant de les utiliser.
from bottle import get, post, put, delete #...
Routes dynamiques
Jokers
Une route dynamique est une route qui permet d'associer plusieurs URLs à une
même fonction, en utilisant des "jokers" dans l'URL. Ces jokers sont écris sous
la forme <nom> et "capturent" dans un paramètre du même nom la valeur
correspondante dans l'URL, jusqu'au prochain slash. Par exemple, la route
/hello/<name> va accepter les demandes /hello/alice et /hello/bob, mais
pas /hello/, ni /hello, ni /hello/alice/bob.
Le ou les "jokers" sont passés par nom à la fonction python.
@route('/hello/<name>')
def hello(name):
return f"Hello {name}!"
Plusieurs "jokers" peuvent être utilisés dans la même route :
@route('/hello/<action>/<user>')
Filtres
Des filtres peuvent être ajoutés aux jokers pour restreindre les valeurs qui vont "matcher" :
:intcorrespond uniquement aux chiffres (signés) et convertit la valeur en entier.:floatsimilaire à:intmais pour les nombres décimaux.:pathcorrespond à tous les caractères, y compris le caractère slash, et peut être utilisé pour correspondre à plus d'un segment de chemin.:revous permet de spécifier une expression régulière1 personnalisée dans le champ de configuration. La valeur appariée n'est pas modifiée.
Exemples de filtres :
@route('/plus/<a:int>/<b:int>'): le jokeraetbdoivent être des entiers, et il répondra à la requête/plus/2/3aveca=2etb=3(nombres entiers) mais pas à la requête/plus/2.5/3.5ni à la requête/plus/toto/titi@route('/plus/<a:float>/<b:float>'): le jokeraetbdoivent être des flottants, et il répondra à la requête/plus/2.5/3.5aveca=2.5etb=3.5(nombres flottants) mais pas à la requête/plus/toto/3.@route('/file/<name:path>'): le jokernamepeut contenir des slashes, il répondra à la requête/file/mon/fichier.txtavecname='mon/fichier.txt'@route('/code/<id:re:[A-Z0-9]+>'): le jokeriddoit être un code ne comportant que des lettres majuscules et des chiffres, et il répondra à la requête/code/ABC123avecid='ABC123', mais pas à la requête/code/abc123ou/code/AB.CD
Une expression régulière (ou expression rationnelle ou expression normale ou motif) est une chaîne de caractères qui décrit, selon une syntaxe précise, un ensemble de chaînes de caractères possibles. Voir fr.wikipedia.org/wiki/Expression_régulière et https://docs.python.org/fr/3/howto/regex.html pour plus de détails. https://regex101.com/ est un site très pratique pour tester les expressions régulières.
En pratique
Bonjour personnalisé
Dans notre serveur, rajouter une route de bonjour personnalisé qui répond à une requète du type
/hello/bob en renvoyant un message du type "Hello bob !"
Correction
# Bonjour personnalisé
@route('/hello/<name>')
def hello_user(name):
return f"Hello {name} !"
Calcul
Ajouter aussi une route de calcul qui répond à une requète du type /calcul/add/3/2.5 et
qui renvoie le résultat sous la forme "resultat = 5.5" (on utilisera "add",
"sub", "mul" et "div" pour addition, soustraction, multiplication et
division), l'opération étant 1er arg opérateur 2ème arg (ex : 3 add 2.5 =
3 + 2.5 = 5.5). Si l'opérateur n'est pas reconnu, renvoyer "opérateur
inconnu".
Correction
# Calcul
@route('/calcul/<op>/<a:float>/<b:float>')
def calcul(op, a, b):
match op:
case 'add':
return f"resultat = {a + b}"
case 'sub':
return f"resultat = {a - b}"
case 'mul':
return f"resultat = {a * b}"
case 'div':
return f"resultat = {a / b}"
case _:
return "opérateur inconnu"