Introduction aux Frameworks Web Python et Asynchronisme
Flask est un micro-framework web pour Python, reconnu pour sa simplicité et sa flexibilité. Cependant, pour des applications nécessitant une haute performance et des opérations non bloquantes, des frameworks asynchrones comme Sanic, Quart ou FastAPI sont souvent privilégiés. Quart, par exemple, est une implémentation asynchrone de l'API Flask, permettant l'utilisation de async et await.
Exemple d'Application Asynchrone avec Quart
Voici comment structurer une application asynchrone basique avec Quart :
from quart import Quart, jsonify, render_template_string
async_app = Quart(__name__)
@async_app.route("/status")
async def fetch_status():
return jsonify({"status": "active", "framework": "Quart"})
@async_app.route("/dashboard")
async def render_dashboard():
html_content = "<h1>Tableau de Bord</h1>"
return await render_template_string(html_content)
if __name__ == "__main__":
async_app.run(port=8080)
Gestion d'asyncio dans Flask
Bien que Flask soit traditionnellement synchrone, il est possible d'intégrer des tâches asynchrones en gérant manuellement la boucle d'événements asyncio. Cela peut être utile pour exécuter des fonctions d'E/S non bloquantes au sein d'une vue Flask.
import asyncio
from flask import Flask, jsonify
flask_app = Flask(__name__)
async def fetch_data_async():
await asyncio.sleep(0.1)
return {"data": "async_result"}
@flask_app.route('/data')
def get_data():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
result = loop.run_until_complete(fetch_data_async())
return jsonify(result)
finally:
loop.close()
Déploiement WSGI et Haute Concurrence
Pour la production, le serveur de développement intégré de Flask ne doit jamais être utilisé. L'architecture standard repose sur un serveur WSGI (Web Server Gateway Interface) comme Gunicorn ou uWSGI, souvent placé derrière un proxy inverse comme Nginx.
Pour augmenter la concurrence, l'utilisation de gevent avec son module pywsgi permet de basculer vers un modèle asynchrone basé sur les coroutines (greenlets), améliorant ainsi la capacité de traitement des requêtes bloquantes.
from gevent import monkey
from gevent.pywsgi import WSGIServer
from multiprocessing import Process, cpu_count
from flask import Flask, jsonify
import os
monkey.patch_all()
concurrent_app = Flask(__name__)
@concurrent_app.route("/health")
def health_check():
return jsonify({"pid": os.getpid(), "status": "healthy"})
def start_server():
server = WSGIServer(("127.0.0.1", 5000), concurrent_app)
server.serve_forever()
if __name__ == "__main__":
for _ in range(cpu_count()):
Process(target=start_server).start()
Routage, Endpoints et Variables d'URL
Le système de routage de Flask mappe les URLs aux fonctions de vue. Les endpoints permettent de référencer ces routes de manière unique, ce qui est particulièrement utile pour les redirections et la génération d'URLs via url_for. Les variables dnyamiques peuvent être capturées et converties directement dans la définition de la route.
from flask import Flask, redirect, url_for, Blueprint
main_app = Flask(__name__)
api_bp = Blueprint('api', __name__, url_prefix='/api')
@api_bp.route('/users/<int:user_id>', endpoint='user_detail')
def get_user(user_id):
return f"Détails de l'utilisateur {user_id}"
@main_app.route('/')
def home():
return redirect(url_for('api.user_detail', user_id=42))
main_app.register_blueprint(api_bp)
Contextes d'Application et de Requête
Flask utilise des contextes pour rendre certaines variables globales accessibles de manière thread-safe. Le contexte d'application (current_app) et le contexte de requête (request) sont gérés via des piles. L'objet g est utilisé pour stocker des données temporaires pendant la durée de vie d'une seule requête.
from flask import Flask, g, request
context_app = Flask(__name__)
@context_app.before_request
def load_user_info():
g.user_role = request.headers.get('X-User-Role', 'guest')
@context_app.route('/profile')
def show_profile():
role = getattr(g, 'user_role', 'unknown')
return f"Rôle actuel : {role}"
Rendu de Templates et Sécurité (XSS)
Flask s'intègre nativement avec le moteur de template Jinja2. Pour prévenir les attaquse Cross-Site Scripting (XSS), Jinja2 active l'échappement automatique par défaut. Les filtres permettent de manipuler les données avant leur rendu.
from flask import Flask, render_template_string, Markup
template_app = Flask(__name__)
@template_app.template_filter('reverse_string')
def reverse_string_filter(text):
return text[::-1]
@template_app.route('/view')
def view_page():
user_input = "<script>alert('XSS')</script>"
safe_input = Markup(user_input).striptags()
template = """
<p>Entrée sécurisée : {{ safe_data }}</p>
<p>Texte inversé : {{ 'Flask' | reverse_string }}</p>
"""
return render_template_string(template, safe_data=safe_input)
Modularisation avec les Blueprints
Pour les applications de grande taille, les Blueprints permettent de diviser le code en modules distincts. Un Blueprint peut avoir ses propres routes, templates et fichiers statiques, et est enregistré sur l'application principale avec un préfixe d'URL optionnel.
from flask import Flask, Blueprint
app_core = Flask(__name__)
auth_module = Blueprint('auth', __name__, template_folder='templates_auth')
@auth_module.route('/login')
def login_view():
return "Page de connexion"
app_core.register_blueprint(auth_module, url_prefix='/auth')