Serveur Web en Python avec flask
#
Il existe de nombreux framework Web pour Python : Django, Cherrypy, Pyramid, Flask, Bottle… Nous allons nous concentrer ici sur flask en raison de sa relative simplicité d’utilisation, et de sa bonne diffusion.
Site officiel de flask : https://flask.palletsprojects.com/en/3.0.x/
La version de flask utilisée lors de la rédaction de ce document est la 2.3.X
Testez si le module est présent avec import flask
.
Si ce n’est pas le cas, un simple pip install flask
devrait fonctionner.
Premiers tests, servir l’heure #
#!/bin/env python3
import flask
import datetime
app = flask.Flask(__name__)
@app.route('/time')
def index():
formatstr = "Nous sommes le %d/%m/%Y, il est %H:%M:%S"
heure = datetime.datetime.now().strftime(formatstr)
stri = "<h1>Horloge</h1>"+heure
return stri
# ============================================================
if __name__ == '__main__':
app.config['DEBUG'] = True
app.run(host='0.0.0.0', port=5000)
flask
contient un serveur Web de test (pour une application importante,
l’application Flask est généralement placée derrière un serveur capable de supporter
une charge élevée (Apache ou Nginx par exemple)). Ce serveur Web est
lancé par la dernière ligne :
app.run(host='0.0.0.0', port=5000)
Il sera accessible sur le port 5000, depuis n’importe quelle IP
(host='0.0.0.0'
).
L’utilisation de flask
consiste à écrire des fonctions, et à mapper
ces fonctions sur des URLs.
Dans l’exemple qui précède, nous avons écrit la fonction index()
et
l’avons associé à l’URL /time
.
Après lancement de l’application, par exemple en local, on peut donc
consulter l’URL : http://localhost:5000/time
. Flask exécutera alors
la fonction index
et renverra au client ce que renvoie la fonction. La
partie HTTP est entièrement gérée par Flask. Nous avons juste à écrire
le texte (généralement une page HTML) qui sera renvoyé.
Et c’est tout ! Ce principe permet déjà de répondre à la plupart des besoins.
Flask
permet de faire beaucoup plus :
- Système de templates (Jinja2), pour ne pas noyer son code Python dans du Html et avoir une maintenance plus simple
- Gestion des cookies
- Gestion des requêtes GET et POST, des formulaires
- Passage de paramètres aux fonctions par le biais de l’URL
Nous allons voir certaines de ces fonctionnalités dans la suite.
Utilisation des templates #
Un template permet d’isoler les parties les plus statiques de la page et d’injecter dans cette page le contenu dynamique. Toutes les pages Web ayant une structure commune, chaque site ayant des réglages communs, un thème etc… ces informations doivent idéalement figurer à un seul endroit du code.
L’utilisation d’un template se fait par le biais de la fonction
render_template
qu’il est commode de renommer :
TPL = flask.render_template
TPL = flask.render_template
...
@app.route('/time')
def index():
formatstr = "Nous sommes le %d/%m/%Y, il est %H:%M:%S"
heure = datetime.datetime.now().strftime(formatstr)
return TPL("page.html", title="Horloge", data=heure)
Le fichier page.html
peut être placé par exemple
dans le répertoire templates
(cet endroit est configuré lors
de la création de l’application Flask) :
<!doctype html>
<!-- page.html -->
<HTML lang="fr">
<HEAD>
<TITLE>{{title}}</TITLE>
<meta charset="UTF-8">
</HEAD>
<body>
<h1>{{title}}</h1>
{{data | safe}}
<hr/>
<font size="-1"><i>Page réalisée avec Flask</i></font>
</body>
</html>
La fonction index
renvoie maintenant le résultat de render_templates
, qui
elle même prend en argument un template, ainsi que des arguments nommés qui
seront injectés dans le template (ici title
et data
).
L’utilisation de {{data | safe}}
ou lieu de {{data}}
indique que le
contenu de la variable data
….XXXXXX
Un formulaire avec Flask #
Voici comment utiliser les informations d’un formulaire. Une première page présente le formulaire (rien de spécial à signaler)
@app.route("/qui")
def qui() :
stri = """
<form method='post' action='bonjour'>
<input type='text' name='nom' placeholder='Votre nom ?'/>
<input type='submit' value='Bonjour Flask !'/>
</form>
"""
return TPL("page.html", title = "Présentez-vous", data=stri)
L’URL appelée lors de la validation est /bonjour
, avec la méthode
POST
:
@app.route("/bonjour", methods=['POST'])
def bonjour() :
nom = flask.request.form['nom']
stri = "Bonjour mon(a) che(è)r(e) {}".format(nom)
return TPL("page.html", title="Bonjour", data=stri)
Il faut préciser methods=['POST']
car par défaut, les routes ne
concernent que les requêtes de type GET
. Une fonction peut aussi
répondre aux deux types de requêtes en indiquant :
methods=['POST', 'GET']
.
Les valeurs entrées dans le formulaire sont récupérables simplement avec :
nom = flask.request.form['nom']
Passage de paramètres aux fonctions #
Une fonctionnalité intéressante permet de passer des portions de l’URL comme paramètres de la fonction :
@app.route("/menu")
def menu() :
stri = """<ul>
<li><a href='/action/temp'>Température</a></li>
<li><a href='/action/pluie'>Pluie</a></li>
</ul>
"""
return TPL("page.html", title="Site Météo", data=stri)
@app.route("/action/<nom_action>")
def action(nom_action) :
if nom_action == 'temp':
stri = "Il ne fera pas très froid"
elif nom_action == 'pluie':
stri = "Il ne pleuvra pas trop. Ou l'inverse"
else:
stri = "Demande erronée"
stri += "<hr/> <a href='../menu'>Retour menu</a>"
return TPL("page.html", title="Site Météo", data=stri)
La route donnée pour la seconde fonction indique que la fonction
action
est associées aux URLs de type : /action/XXXX
où XXX
est
passé en paramètre nommé à la fonction.
Notons que l’utilisation de l’URL /action
ne fonctionnera pas (mais on
peut associer plusieurs URLs à une seule fonction, comme indiqué dans le
manuel).
Code complet #
#!/bin/env python3
import flask
import datetime
TPL = flask.render_template
app = flask.Flask(__name__)
@app.route('/time')
def index():
formatstr = "Nous sommes le %d/%m/%Y, il est %H:%M:%S"
heure = datetime.datetime.now().strftime(formatstr)
return TPL("page.html", title="Horloge", data=heure)
@app.route("/qui")
def qui() :
stri = """
<form method='post' action='bonjour'>
<input type='text' name='nom' placeholder='Votre nom ?'/>
<input type='submit' value='Bonjour Flask !'/>
</form>
"""
return TPL("page.html", title = "Présentez-vous", data=stri)
@app.route("/bonjour", methods=['POST'])
def bonjour() :
nom = flask.request.form['nom']
stri = "Bonjour mon(a) che(è)r(e) {}".format(nom)
return TPL("page.html", title="Bonjour", data=stri)
@app.route("/menu")
def menu() :
stri = """<ul>
<li><a href='/action/temp'>Température</a></li>
<li><a href='/action/pluie'>Pluie</a></li>
</ul>
"""
return TPL("page.html", title="Site Météo", data=stri)
@app.route("/action/<nom_action>")
def action(nom_action) :
if nom_action == 'temp':
stri = "Il ne fera pas très froid"
elif nom_action == 'pluie':
stri = "Il ne pleuvra pas trop. Ou l'inverse"
else:
stri = "Demande erronée"
stri += "<hr/> <a href='../menu'>Retour menu</a>"
return TPL("page.html", title="Site Météo", data=stri)
# ============================================================
if __name__ == '__main__':
app.config['DEBUG'] = True
app.run(host='0.0.0.0', port=5000)
Pensez à récupérer aussi le fichier template : page.html
.
Code complet / un template par page #
Il peut être préférable d’avoir un template par page. On peut dans ce cas utiliser
la directive {% extends "XXX.html" %}
dans le template, pour baser
chaque template particulier sur un template plus général.
(Voir la documentation)
Servir de pages statiques #
Si votre site contient des images ou des feuilles de style, elles
peuvent aussi être servies par Flask. Il suffit de les déposer dans le
dossier nommé static
à la racine du site.
Autre exemple complet Flask #
Example flask contenant :
- redirection
- code d’erreur (ici 403) avec
abort
- récupération des paramètres GET
- récupération des paramètres POST
- réalisation d’un formulaire
- utilisation d’un template Jinja2
- utilisation de
url_for
- renvoi de données au format Json (pour Api Rest)
Enregistrer les deux fichiers dans le même répertoire :
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- ======================= head ========================== -->
<title>Titre : {{ title }}</title>
</head>
<body>
<div>
{{ data | safe }}
</div>
<div id="footer">
<hr/>
<a href="{{ url_for('info') }}">home</a>
- <a href="{{ url_for('paramurl', number=42) }}">paramurl</a>
- <a href="{{ url_for('formulaire') }}">formulaire</a>
- <a href="{{ url_for('paramget') }}?login=toto">paramget</a>
- <a href="{{ url_for('data_json') }}">data_json</a>
- <a href="{{ url_for('redirect_me') }}">redirect_me</a>
- <a href="{{ url_for('forbidden') }}">forbidden</a>
</div>
</body>
</html>
#!/bin/env python3
import flask
TPL = flask.render_template # Pour éviter de toujours taper flask.render_template...
app = flask.Flask(__name__, template_folder='.')
@app.route('/')
def info():
data = """\
Bonjour Visiteur
"""
return TPL("default.html", title='Home', data=data)
@app.route('/paramurl/<int:number>')
def paramurl(number):
data = """\
Vous avez mis {} dans l'URL.
""".format(number)
return TPL("default.html", title='ParamUrl', data=data)
@app.route('/paramget')
def paramget():
login = flask.request.args['login']
data = """\
Le login entré est {}.
""".format(login)
return TPL("default.html", title='Paramget', data=data)
@app.route('/formulaire')
def formulaire():
data = """\
<form action="validate" method="post">
<input type="text" name="login"/><br/>
<input type="submit"/>
</form>
"""
return TPL("default.html", title='Formulaire', data=data)
# Récupération des données d'un formulaire, en POST uniquement
# Si les données sont postées en json et non en
# application/x-www-form-urlencoded ou multipart/form-data
# utiliser flask.request.get_json() au lieu
# de flask.request.form
@app.route('/validate', methods=["POST"])
def validate():
login = flask.request.form['login']
data = """\
Le login entré est {}.
""".format(login)
return TPL("default.html", title='Validate', data=data)
# Renvoie du json
@app.route('/data_json')
def data_json():
liste = [1, 2, 3, "toto"]
dico = {"val1": liste, "val2": "Salut"}
return flask.jsonify(dico) # Renvoie la chaîne json en gérant correctement l'entête HTTP
# Code de retour :
# https://fr.wikipedia.org/wiki/Liste_des_codes_HTTP
@app.route('/forbidden')
def forbidden():
flask.abort(403)
@app.route('/redirect_me')
def redirect_me():
return flask.redirect(flask.url_for('info'))
print("PATH =====>", app.instance_path)
if __name__ == '__main__':
app.config['DEBUG'] = True
app.secret_key = 'chooseaverysecretkeyhere'
app.run(host='0.0.0.0', port=5000)