Développement d'APIs RESTful avec l'extension Flask-Restful

Flask-Restful est une extension pour le framework Flask qui facilite la création rapide d'APIs respectant l'architecture REST. Elle encourage les bonnes pratiques en proposant une structuration par ressources et fournit des outils intégrés pour la validation des entrées et le formatage des sorties.

Installation du framework

Pour intégrer Flask-Restful à votre environnement Python, vous pouvez utiliser le gestionnaire de paquets pip :

pip install flask-restful

Si vous préférez installer la version de développement depuis les sources :

git clone https://github.com/twilio/flask-restful.git
cd flask-restful
python setup.py install

Mise en œuvre d'une ressource de base

L'unité de base dans Flask-Restful est la Resource. Chaque classe héritant de Resource peut définir des méthodes correspondant aux verbes HTTP (GET, POST, PUT, DELETE).

from flask import Flask
from flask_restful import Api, Resource

app = Flask(__name__)
api = Api(app)

class StatutServeur(Resource):
   def get(self):
       return {'etat': 'actif', 'version': '1.0.2'}

api.add_resource(StatutServeur, '/status')

if __name__ == '__main__':
   app.run(debug=True)

Gestion des entrées avec RequestParser

Flask-Restful propose l'outil reqparse pour extraire et valider les paramètres envoyés par le client, qu'ils soient dans l'URL, le corps de la requête (JSON/Form) ou les en-têtes.

from flask_restful import reqparse, Resource

# Configuration de l'analyseur de requêtes
analyseur = reqparse.RequestParser()
analyseur.add_argument('titre', type=str, required=True, help='Le titre est obligatoire')
analyseur.add_argument('priorite', type=int, default=1, location='json')
analyseur.add_argument('tags', type=str, action='append') # Pour accepter plusieurs valeurs

class GestionTache(Resource):
   def post(self):
       donnees = analyseur.parse_args()
       # Traitement des données validées
       return {"message": "Tâche créée", "data": donnees}, 201

Options courantes de add_argument :

  • dest : Définit le nom de la clé dans le dictionnaire de sortie.
  • location : Définit où chercher la valeur (args, json, form, headers, cookies).
  • choices : Liste de valeurs autorisées.
  • trim : Suppirme les espaces avant et après la chaîne de caractères.

Formatage des données de sortie (Serialization)

Pour contrôler précisément la structure JSON renvoyée au client, on utilise le module fields combiné au décorateur marshal_with.

from flask_restful import fields, marshal_with, Resource
import datetime

# Définition de la structure de sortie
modele_utilisateur = {
   'id_public': fields.Integer(attribute='id'),
   'nom_complet': fields.String(attribute='username'),
   'date_inscription': fields.DateTime(dt_format='iso8601'),
   'est_actif': fields.Boolean(default=True),
   'url_profil': fields.Url('detail_utilisateur', absolute=True)
}

class Utilisateur:
   def __init__(self, id, username):
       self.id = id
       self.username = username
       self.date_inscription = datetime.datetime.now()

class DetailUtilisateur(Resource):
   @marshal_with(modele_utilisateur)
   def get(self, user_id):
       # Simulation d'un objet métier
       user_obj = Utilisateur(id=user_id, username="JeanDupont")
       return user_obj

api.add_resource(DetailUtilisateur, '/user/<int:user_id>', endpoint='detail_utilisateur')

Champs personnalisés et imbriqués

Il est fréquent de devoir renvoyer des structures complexes ou de transformer une donnée avant l'envoi.

class ChampPrix(fields.Raw):
   def format(self, value):
       return f"{value:.2f} EUR"

detail_produit = {
   'sku': fields.String,
   'nom': fields.String
}

reponse_commande = {
   'commande_id': fields.Integer,
   'total': ChampPrix(attribute='montant'),
   'articles': fields.List(fields.Nested(detail_produit))
}

Personnalisation globale de la réponse JSON

Si vous souhaitez que toutes vos réponses API suivent un format standard (par exemple, inclure un code de statut personnalisé), vous pouvez surcharger la méthode de représentation de l'objet API.

from flask import make_response
import json

def formatage_standard(data, code, headers=None):
   enveloppe = {
       'status': 'success' if code < 400 else 'error',
       'content': data
   }
   reponse = make_response(json.dumps(enveloppe), code)
   reponse.headers.extend(headers or {})
   return reponse

class ApiPersonnalisee(Api):
   def __init__(self, *args, **kwargs):
       super().__init__(*args, **kwargs)
       self.representations = {
           'application/json': formatage_standard
       }

Utilisation avec les Blueprints

Pour les applications de grande envergure, il est recommandé d'utiliser les Blueprints Flask pour organiser le code par modules.

from flask import Blueprint

api_bp = Blueprint('v1', __name__)
api_v1 = Api(api_bp)

class RessourceModule(Resource):
   def get(self):
       return {'info': 'Réponse du module v1'}

api_v1.add_resource(RessourceModule, '/info')

# Dans le fichier principal app.py
# app.register_blueprint(api_bp, url_prefix='/api/v1')

Injection de dépendances dans les ressources

Vous pouvez passer des arguments externes (comme une instance de base de données ou de cache) à vos constructeurs de ressourcse via resource_class_kwargs.

class ServiceExterne(Resource):
   def __init__(self, connecteur):
       self.db = connecteur

   def get(self):
       return self.db.get_stats()

api.add_resource(ServiceExterne, '/stats', 
                resource_class_kwargs={'connecteur': mon_objet_db})

Étiquettes: Flask-Restful Python api-rest backend JSON-Serialization

Publié le 11 juin à 00h30