La génération d'une documentation API complète et précise est une partie essentielle mais souvent fastidieuse du développement logiciel. La spécification OpenAPI (anciennement connue sous le nom de Swagger) s'est imposée comme la norme industrielle pour définir les API RESTful. Elle fournit un format lisible par machine qui permet aux humains comme aux ordinateurs de découvrir et de comprendre les capacités d'un service sans avoir accès au code source, à la documentation ou par inspection du trafic réseau.1
Alors que de nombreux frameworks offrent des plugins pour générer des spécifications OpenAPI à partir d'annotations de code (comme les docstrings), il existe des scénarios où vous pourriez avoir besoin d'un contrôle programmatique plus direct sur la création de la spécification. Cela pourrait être dû au fait que vous travaillez avec un système hérité, un framework non standard, ou que vous devez générer une spécification pour une API composée de plusieurs microservices.
C'est là qu'intervient pyswagger
. C'est une puissante bibliothèque Python qui fonctionne comme une boîte à outils pour OpenAPI. Bien qu'elle soit souvent utilisée comme client API pour consommer des services définis par une spécification OpenAPI, sa véritable puissance réside dans son modèle objet, qui vous permet de construire, manipuler et valider une spécification par programmation.
Dans ce tutoriel complet, nous allons parcourir le processus d'utilisation de pyswagger
pour générer manuellement, mais automatiquement, une spécification OpenAPI 3.0 complète pour une simple application web Python construite avec Flask. Nous construirons la spécification à partir de zéro, pièce par pièce, démontrant comment les objets de pyswagger
correspondent directement aux composants de la norme OpenAPI. À la fin, vous aurez non seulement un fichier openapi.json
généré, mais aussi une interface utilisateur de documentation interactive et en direct servie directement depuis votre application.
Vous voulez une plateforme intégrée, Tout-en-Un pour que votre équipe de développeurs travaille ensemble avec une productivité maximale ?
Apidog répond à toutes vos demandes et remplace Postman à un prix beaucoup plus abordable !
Partie 1 : Configuration de l'environnement du projet
Avant de pouvoir commencer à générer notre spécification, nous devons configurer un environnement de développement approprié. Cela implique de créer un environnement Python isolé pour gérer nos dépendances et d'installer les bibliothèques nécessaires.
Création de votre espace de travail ⚙️
Tout d'abord, créons un répertoire pour notre projet. Ouvrez votre terminal ou invite de commandes et exécutez les commandes suivantes :Bash
# Créer un nouveau répertoire pour notre projet
mkdir pyswagger-tutorial
cd pyswagger-tutorial
# Créer un environnement virtuel Python
# Sur macOS/Linux
python3 -m venv venv
# Sur Windows
python -m venv venv
Un environnement virtuel est une arborescence de répertoires autonome qui comprend une installation Python et un certain nombre de fichiers de support. L'utilisation d'un environnement virtuel garantit que les paquets que nous installons pour ce projet n'entrent pas en conflit avec les paquets installés pour d'autres projets.
Maintenant, activez l'environnement virtuel :Bash
# Sur macOS/Linux
source venv/bin/activate
# Sur Windows
.\venv\Scripts\activate
Une fois activé, l'invite de votre terminal devrait changer pour afficher le nom de l'environnement virtuel (par exemple, (venv)
), indiquant que vous travaillez maintenant à l'intérieur de celui-ci.
Installation des bibliothèques nécessaires
Avec notre environnement actif, nous pouvons installer les bibliothèques Python dont nous aurons besoin pour ce tutoriel. Nous avons besoin de pyswagger
lui-même pour construire la spécification, de Flask
pour créer notre simple API web, et de PyYAML
car pyswagger
l'utilise pour les opérations YAML.Bash
pip install "pyswagger[utils]" Flask PyYAML
La partie [utils]
lors de l'installation de pyswagger
est une bonne pratique car elle inclut des utilitaires utiles, comme un validateur que nous utiliserons plus tard pour vérifier la justesse de notre spécification générée.
Pour une bonne gestion de projet, il est judicieux d'épingler vos dépendances dans un fichier requirements.txt
.Bash
pip freeze > requirements.txt
Votre fichier requirements.txt
contiendra maintenant les bibliothèques et leurs versions spécifiques, rendant votre projet facilement reproductible par d'autres.
Création de l'application Flask de base
Maintenant, créons une application Flask minimale. Cela servira de base à l'API que nous allons documenter.
Dans le répertoire de votre projet, créez un nouveau fichier nommé app.py
et ajoutez le code suivant :Python
# app.py
from flask import Flask, jsonify
# Initialiser l'application Flask
app = Flask(__name__)
@app.route("/")
def index():
""" Un simple point de terminaison pour vérifier si l'application est en cours d'exécution. """
return jsonify({"message": "API is up and running!"})
if __name__ == "__main__":
# Exécute l'application Flask sur http://127.0.0.1:5000
app.run(debug=True)
Ce code met en place un serveur web très simple avec un seul point de terminaison. Pour l'exécuter, assurez-vous que votre environnement virtuel est toujours actif et exécutez la commande suivante dans votre terminal :Bash
python app.py
Vous devriez voir une sortie indiquant que le serveur est en cours d'exécution, quelque chose comme ceci :
* Serving Flask app 'app'
* Debug mode: on
* Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
Vous pouvez maintenant ouvrir votre navigateur web ou utiliser un outil comme curl
pour visiter http://127.0.0.1:5000
. Vous devriez voir la réponse JSON : {"message": "API is up and running!"}
.
Avec notre environnement de base et le squelette de l'application en place, nous pouvons maintenant plonger dans les concepts fondamentaux de pyswagger
.
Partie 2 : Comprendre le modèle objet de pyswagger
Pour utiliser efficacement pyswagger
afin de générer une spécification, vous devez d'abord comprendre comment son modèle objet correspond à la structure d'un document OpenAPI. Une spécification OpenAPI est essentiellement un grand objet JSON ou YAML avec un schéma spécifique. pyswagger
fournit des classes et des objets Python qui reflètent ce schéma, vous permettant de construire la spécification d'une manière plus intuitive et orientée objet.
Concepts fondamentaux de la spécification OpenAPI 3.0 📜
Un document OpenAPI 3.0 possède quelques champs clés de niveau supérieur :
openapi
: Une chaîne de caractères spécifiant la version de la spécification OpenAPI (par exemple,'3.0.0'
).info
: Un objet fournissant des métadonnées sur l'API. Cela inclut letitle
, laversion
, ladescription
et les informations de contact.servers
: Un tableau d'objets serveur, qui définissent les URL de base pour l'API.paths
: Le champ le plus important. Cet objet contient tous les points de terminaison (paths) API disponibles et les opérations HTTP (GET, POST, PUT, DELETE, etc.) qui peuvent y être effectuées.components
: Un objet qui contient un ensemble d'objets réutilisables pour différentes parties de la spécification. C'est essentiel pour garder votre spécification DRY (Don't Repeat Yourself - Ne vous répétez pas). Vous pouvez définir desschemas
(modèles de données) réutilisables, desresponses
, desparameters
, desexamples
, et plus encore.
Mappage d'OpenAPI aux objets pyswagger
pyswagger
fournit un mappage clair de ces concepts OpenAPI aux objets Python. Explorons les principaux que nous utiliserons.
L'objet central dans pyswagger
est l'objet App
. Vous pouvez considérer une instance App
comme la racine de votre document OpenAPI.Python
from pyswagger import App
# La racine du document de spécification
# Nous l'initialisons avec une version, mais il peut aussi charger à partir d'une URL ou d'un fichier
root_app = App(version='3.0.0')
Une fois que vous avez votre objet App
, vous pouvez commencer à remplir ses attributs, qui correspondent directement aux champs OpenAPI. pyswagger
utilise un modèle de construction (builder pattern), permettant une syntaxe fluide et lisible.
Info et Serveurs
Les sections info
et servers
sont simples à remplir.Python
# Remplir l'objet 'info'
root_app.info.title = "User API"
root_app.info.version = "1.0.0"
root_app.info.description = "A simple API to manage users, used for the pyswagger tutorial."
# Remplir le tableau 'servers'
# Vous créez un objet Server et l'ajoutez
server = root_app.prepare_obj('Server', {'url': 'http://127.0.0.1:5000', 'description': 'Serveur de développement local'})
root_app.servers.append(server)
Paths et Opérations
Les paths sont définis sur l'objet App
. Vous ajoutez de nouveaux paths, puis définissez les opérations (méthodes HTTP) à l'intérieur. Chaque opération est configurée avec des détails tels qu'un summary
, une description
, des parameters
, un requestBody
et des responses
.Python
# Définir un path et une opération
# Cela n'exécute rien ; cela construit juste la structure de l'objet.
path_item = root_app.define_path('/users')
get_op = path_item.define_op('get')
get_op.summary = "Retrieve a list of all users"
Composants : Schémas, Paramètres et Réponses
La véritable puissance d'une spécification OpenAPI bien structurée provient des composants réutilisables. Au lieu de définir la structure d'un objet "User" chaque fois qu'il apparaît dans une réponse, vous le définissez une fois dans components/schemas
, puis vous y faites référence à l'aide d'un pointeur $ref
. pyswagger
gère cela élégamment.
Schema
: Un objet Schema
définit un modèle de données. Vous pouvez spécifier son type (object
, string
, integer
) et ses propriétés.Python
# Préparer un objet Schema pour un Utilisateur
user_schema = root_app.prepare_obj('Schema', {
'type': 'object',
'properties': {
'id': {'type': 'integer', 'format': 'int64'},
'username': {'type': 'string'},
'email': {'type': 'string', 'format': 'email'}
},
'required': ['id', 'username', 'email']
})
# L'ajouter aux composants réutilisables
root_app.components.schemas['User'] = user_schema
Parameter
: Un objet Parameter
définit un paramètre d'opération unique. Vous spécifiez son nom, où il se trouve (in
: 'path'
, 'query'
, 'header'
, ou 'cookie'
), et son schéma.Python
# Préparer un objet Parameter pour un ID utilisateur dans le path
user_id_param = root_app.prepare_obj('Parameter', {
'name': 'user_id',
'in': 'path',
'description': 'ID de l\'utilisateur à récupérer',
'required': True,
'schema': {'type': 'integer'}
})
Response
: Un objet Response
définit la structure d'une réponse pour un code de statut HTTP spécifique. Il inclut une description
et le content
, qui spécifie le type de média (par exemple, application/json
) et son schéma.Python
# Préparer un objet Response pour une réponse 200 OK retournant un seul utilisateur
# Notez l'utilisation de '$ref' pour pointer vers notre schéma User réutilisable
ok_user_response = root_app.prepare_obj('Response', {
'description': 'Récupération réussie d\'un utilisateur',
'content': {
'application/json': {
'schema': {'$ref': '#/components/schemas/User'}
}
}
})
Comprendre ce mappage est la clé pour construire votre spécification. Vous construisez essentiellement un graphe d'objets Python que pyswagger
sérialisera plus tard en un fichier JSON ou YAML OpenAPI valide.
Partie 3 : Construire une API simple avec Flask
Pour rendre notre exercice de documentation pratique, nous avons besoin d'une API réelle à documenter. Nous allons étendre notre simple application Flask de la Partie 1 en une API REST minimale pour gérer une liste d'utilisateurs. Cette API servira de "source de vérité" que nous décrirons avec pyswagger
.
Conception d'une API simple "Utilisateur" 📝
Nous allons implémenter quatre points de terminaison de base qui représentent les opérations CRUD (Créer, Lire, Mettre à jour, Supprimer) courantes :
GET /users
: Récupérer une liste de tous les utilisateurs.POST /users
: Créer un nouvel utilisateur.GET /users/{user_id}
: Obtenir un seul utilisateur par son ID.DELETE /users/{user_id}
: Supprimer un utilisateur par son ID.
Pour simplifier, nous utiliserons un simple dictionnaire Python comme "base de données" en mémoire. Dans une application réelle, il s'agirait d'une connexion à une base de données comme PostgreSQL ou MongoDB.
Implémentation des points de terminaison Flask
Mettons à jour notre fichier app.py
pour inclure la logique de ces points de terminaison. Remplacez le contenu de app.py
par ce qui suit :Python
# app.py
from flask import Flask, jsonify, request, abort
app = Flask(__name__)
# --- Base de données en mémoire ---
# Un simple dictionnaire pour stocker nos utilisateurs.
# La clé est l'user_id (entier), et la valeur est les données de l'utilisateur (dict).
USERS_DB = {
1: {"username": "alice", "email": "alice@example.com"},
2: {"username": "bob", "email": "bob@example.com"},
3: {"username": "charlie", "email": "charlie@example.com"},
}
# Un compteur pour simuler les ID auto-incrémentés pour les nouveaux utilisateurs
LAST_INSERT_ID = 3
# --- Points de terminaison API ---
@app.route("/users", methods=["GET"])
def get_users():
""" Retourne une liste de tous les utilisateurs. """
# Nous devons convertir le dictionnaire en une liste d'objets utilisateur, incluant leurs ID.
users_list = []
for user_id, user_data in USERS_DB.items():
user = {'id': user_id}
user.update(user_data)
users_list.append(user)
return jsonify(users_list)
@app.route("/users", methods=["POST"])
def create_user():
""" Crée un nouvel utilisateur. """
global LAST_INSERT_ID
if not request.json or 'username' not in request.json or 'email' not in request.json:
abort(400, description="Missing username or email in request body.")
LAST_INSERT_ID += 1
new_user_id = LAST_INSERT_ID
new_user = {
"username": request.json["username"],
"email": request.json["email"],
}
USERS_DB[new_user_id] = new_user
# La réponse devrait inclure l'ID de l'utilisateur nouvellement créé
response_user = {'id': new_user_id}
response_user.update(new_user)
return jsonify(response_user), 201
@app.route("/users/<int:user_id>", methods=["GET"])
def get_user(user_id):
""" Retourne un seul utilisateur par son ID. """
if user_id not in USERS_DB:
abort(404, description=f"User with ID {user_id} not found.")
user_data = USERS_DB[user_id]
user = {'id': user_id}
user.update(user_data)
return jsonify(user)
@app.route("/users/<int:user_id>", methods=["DELETE"])
def delete_user(user_id):
""" Supprime un utilisateur par son ID. """
if user_id not in USERS_DB:
abort(404, description=f"User with ID {user_id} not found.")
del USERS_DB[user_id]
# Une réponse 204 No Content est standard pour les suppressions réussies
return '', 204
if __name__ == "__main__":
app.run(debug=True, port=5000)
Maintenant, si vous exécutez à nouveau python app.py
, vous aurez une API entièrement fonctionnelle (bien que simple). Vous pouvez la tester avec curl
ou un outil similaire :
- Obtenir tous les utilisateurs :
curl http://127.0.0.1:5000/users
- Obtenir un utilisateur spécifique :
curl http://127.0.0.1:5000/users/1
- Créer un utilisateur :
curl -X POST -H "Content-Type: application/json" -d '{"username": "david", "email": "david@example.com"}' http://127.0.0.1:5000/users
- Supprimer un utilisateur :
curl -X DELETE http://127.0.0.1:5000/users/3
Avec notre API implémentée, nous avons une cible concrète pour nos efforts de documentation. L'étape suivante consiste à utiliser pyswagger
pour décrire chacun de ces points de terminaison en détail.
Partie 4 : Auto-génération de la spécification OpenAPI avec pyswagger
C'est le cœur de notre tutoriel. Nous allons maintenant créer un script Python séparé qui importe pyswagger
, définit la structure de notre API à l'aide de son modèle objet, puis sérialise cette structure dans un fichier openapi.json
complet. Cette approche découple la génération de la spécification de la logique de l'application, ce qui peut être un modèle très propre et maintenable.
Création du générateur de spécification
Dans le répertoire de votre projet, créez un nouveau fichier nommé generate_spec.py
. Ce script sera responsable de la construction et de la sauvegarde de notre spécification OpenAPI.
Construire la spécification, étape par étape
Construisons le script generate_spec.py
pièce par pièce.
1. Imports et initialisation de l'objet App
Tout d'abord, nous devons importer l'objet App
de pyswagger
et PyYAML
pour aider à l'exportation du fichier final. Nous allons créer notre objet App
racine et remplir les sections de base info
et servers
, comme nous l'avons discuté dans la Partie 2.Python
# generate_spec.py
import json
from pyswagger import App
from pyswagger.contrib.client.requests import Client
# --- 1. Initialiser l'objet App racine ---
app = App(version='3.0.0')
# --- 2. Remplir les sections Info & Servers ---
app.info.title = "User API"
app.info.version = "1.0.0"
app.info.description = "A simple API to manage users, for the pyswagger tutorial."
server = app.prepare_obj('Server', {
'url': 'http://127.0.0.1:5000',
'description': 'Serveur de développement local'
})
app.servers.append(server)
2. Définition des composants réutilisables (Schémas)
Une bonne conception d'API évite la répétition. Nous définirons notre modèle de données User
une seule fois et le réutiliserons. Nous définirons également un schéma Error
générique pour nos réponses d'erreur (comme 404 Not Found).
Ajoutez le code suivant à generate_spec.py
:Python
# --- 3. Définition des composants réutilisables (Schémas) ---
# Schéma pour la réponse d'erreur
error_schema = app.prepare_obj('Schema', {
'type': 'object',
'properties': {
'code': {'type': 'integer', 'format': 'int32'},
'message': {'type': 'string'}
}
})
app.components.schemas['Error'] = error_schema
# Schéma pour un seul utilisateur. Notez que les propriétés correspondent à la structure de notre USERS_DB.
user_schema = app.prepare_obj('Schema', {
'type': 'object',
'properties': {
'id': {
'type': 'integer',
'description': 'Identifiant unique de l\'utilisateur.',
'readOnly': True # Le client ne peut pas définir cette valeur
},
'username': {
'type': 'string',
'description': 'Le nom d\'utilisateur choisi par l\'utilisateur.'
},
'email': {
'type': 'string',
'description': 'L\'adresse e-mail de l\'utilisateur.',
'format': 'email'
}
},
'required': ['id', 'username', 'email']
})
app.components.schemas['User'] = user_schema
# Schéma pour la création d'un utilisateur (n'inclut pas le champ 'id')
new_user_schema = app.prepare_obj('Schema', {
'type': 'object',
'properties': {
'username': {
'type': 'string',
'description': 'Le nom d\'utilisateur choisi par l\'utilisateur.'
},
'email': {
'type': 'string',
'description': 'L\'adresse e-mail de l\'utilisateur.',
'format': 'email'
}
},
'required': ['username', 'email']
})
app.components.schemas['NewUser'] = new_user_schema
3. Documentation des points de terminaison /users
Nous allons maintenant définir le path /users
et ses deux opérations : GET
et POST
.
- Pour
GET
, la réponse est un tableau d'objetsUser
. - Pour
POST
, le corps de la requête attendra un objetNewUser
, et la réponse réussie sera un seul objetUser
(incluant le nouvel ID).
Python
# --- 4. Documentation des Paths ---
# -- Path : /users --
path_users = app.define_path('/users')
# Opération : GET /users
op_get_users = path_users.define_op('get')
op_get_users.summary = "Lister tous les utilisateurs"
op_get_users.description = "Retourne un tableau JSON de tous les objets utilisateur."
op_get_users.tags.append('Users')
op_get_users.responses.A('200').description = "Une liste d'utilisateurs."
op_get_users.responses.A('200').content.A('application/json').schema.A(
'array', items={'$ref': '#/components/schemas/User'}
)
# Opération : POST /users
op_post_users = path_users.define_op('post')
op_post_users.summary = "Créer un nouvel utilisateur"
op_post_users.description = "Ajoute un nouvel utilisateur à la base de données."
op_post_users.tags.append('Users')
op_post_users.requestBody.description = "Objet utilisateur qui doit être ajouté."
op_post_users.requestBody.required = True
op_post_users.requestBody.content.A('application/json').schema.set_ref('#/components/schemas/NewUser')
op_post_users.responses.A('201').description = "Utilisateur créé avec succès."
op_post_users.responses.A('201').content.A('application/json').schema.set_ref('#/components/schemas/User')
op_post_users.responses.A('400').description = "Entrée invalide fournie."
op_post_users.responses.A('400').content.A('application/json').schema.set_ref('#/components/schemas/Error')
Notez l'utilisation de set_ref
et A
(qui signifie "accès") pour une syntaxe plus concise afin de construire la structure d'objets imbriqués.
4. Documentation des points de terminaison /users/{user_id}
Ensuite, nous allons documenter le path pour interagir avec un seul utilisateur. Ce path inclut un paramètre de path, {user_id}
.
- Pour
GET
, nous devrons définir ce paramètre de path. La réponse est un seul objetUser
ou une erreur404
. - Pour
DELETE
, nous avons également besoin du paramètre de path. La réponse réussie est une204 No Content
.
Python
# -- Path : /users/{user_id} --
path_user_id = app.define_path('/users/{user_id}')
# Nous pouvons définir le paramètre une fois et le réutiliser pour toutes les opérations sur ce path.
user_id_param = app.prepare_obj('Parameter', {
'name': 'user_id',
'in': 'path',
'description': 'ID de l\'utilisateur',
'required': True,
'schema': {'type': 'integer'}
})
path_user_id.parameters.append(user_id_param)
# Opération : GET /users/{user_id}
op_get_user_id = path_user_id.define_op('get')
op_get_user_id.summary = "Trouver un utilisateur par ID"
op_get_user_id.description = "Retourne un seul utilisateur."
op_get_user_id.tags.append('Users')
op_get_user_id.responses.A('200').description = "Opération réussie."
op_get_user_id.responses.A('200').content.A('application/json').schema.set_ref('#/components/schemas/User')
op_get_user_id.responses.A('404').description = "Utilisateur introuvable."
op_get_user_id.responses.A('404').content.A('application/json').schema.set_ref('#/components/schemas/Error')
# Opération : DELETE /users/{user_id}
op_delete_user_id = path_user_id.define_op('delete')
op_delete_user_id.summary = "Supprime un utilisateur"
op_delete_user_id.description = "Supprime un seul utilisateur de la base de données."
op_delete_user_id.tags.append('Users')
op_delete_user_id.responses.A('204').description = "Utilisateur supprimé avec succès."
op_delete_user_id.responses.A('404').description = "Utilisateur introuvable."
op_delete_user_id.responses.A('404').content.A('application/json').schema.set_ref('#/components/schemas/Error')
5. Validation et sauvegarde de la spécification
Enfin, l'étape la plus satisfaisante. Nous allons demander à pyswagger
de valider notre graphe d'objets construit par rapport au schéma OpenAPI 3.0. S'il est valide, nous l'exporterons dans un fichier JSON.
Ajoutez ce dernier bloc de code à generate_spec.py
:Python
# --- 5. Valider et sauvegarder la spécification ---
if __name__ == '__main__':
try:
# Valider la spécification générée
app.validate()
print("La spécification est valide.")
# Sauvegarder la spécification dans un fichier JSON
with open('openapi.json', 'w') as f:
f.write(app.dump_json(indent=2))
print("Fichier openapi.json généré avec succès")
except Exception as e:
print(f"Erreur de validation : {e}")
Votre fichier generate_spec.py
est maintenant complet. Exécutez-le depuis votre terminal :Bash
python generate_spec.py
Si tout est correct, vous verrez la sortie :
La spécification est valide.
Fichier openapi.json généré avec succès
Vous aurez maintenant un nouveau fichier, openapi.json
, dans le répertoire de votre projet. Ouvrez-le et explorez son contenu. Vous verrez un document OpenAPI 3.0 parfaitement structuré qui décrit votre API Flask dans les moindres détails.
Partie 5 : Servir la documentation
Avoir un fichier openapi.json
est excellent pour les machines et pour générer des SDK clients, mais pour les développeurs humains, une interface utilisateur interactive est bien plus utile. Dans cette dernière partie, nous allons intégrer notre spécification générée dans notre application Flask et la servir en utilisant le populaire Swagger UI.
Intégration de la spécification dans Flask
Tout d'abord, nous devons créer un point de terminaison dans notre application Flask qui sert le fichier openapi.json
. Cela permet aux outils de documentation de récupérer la spécification directement depuis notre API en cours d'exécution.
Modifiez app.py
pour ajouter une nouvelle route :Python
# app.py
# ... (garder tout le code Flask existant) ...
import os
# ... (toutes les routes comme /users, /users/{user_id}, etc.) ...
# --- Servir la spécification OpenAPI et l'interface utilisateur ---
@app.route('/api/docs/openapi.json')
def serve_openapi_spec():
""" Sert le fichier openapi.json généré. """
# Ceci suppose que openapi.json est dans le même répertoire que app.py
# Dans une application plus grande, vous pourriez vouloir une méthode plus robuste pour trouver le fichier
return app.send_static_file('openapi.json')
Pour que cela fonctionne, Flask doit savoir où trouver les fichiers statiques. Par défaut, il cherche dans un répertoire nommé static
. Créons-en un et déplaçons-y notre fichier openapi.json
.Bash
mkdir static
mv openapi.json static/
Maintenant, exécutez python app.py
, et naviguez vers http://127.0.0.1:5000/api/docs/openapi.json
. Vous devriez voir le JSON brut de votre spécification.
Servir l'interface utilisateur interactive avec Swagger UI 🎨
Swagger UI est une collection d'actifs HTML,2 JavaScript et CSS sans dépendances qui génère dynamiquement une belle documentation à partir d'une API conforme à OpenAPI. Nous pouvons facilement la servir depuis notre application Flask.
Créer un répertoire Templates : Flask utilise un répertoire nommé templates
pour rechercher les modèles HTML.Bash
mkdir templates
Créer le modèle Swagger UI : À l'intérieur du répertoire templates
, créez un nouveau fichier nommé swagger_ui.html
. Collez le code HTML suivant. Ce code charge les actifs de Swagger UI depuis un CDN public et le configure pour charger notre fichier de spécification.HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>API Utilisateur - Swagger UI</title>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.17.14/swagger-ui.min.css" >
<style>
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.17.14/swagger-ui-bundle.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.17.14/swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Début de la région d'appel Swagger3 UI
const ui = SwaggerUIBundle({
url: "/api/docs/openapi.json", // L'URL de notre spécification
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset4
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
// Fin de la région d'appel Swagger UI
window.ui = ui
}
</script>
</body>
</html>
```
Ajouter la route Flask : Enfin, ajoutez une route à app.py
pour rendre ce modèle HTML. Vous devrez importer render_template
depuis Flask.Python
# En haut de app.py
from flask import Flask, jsonify, request, abort, render_template
# ... (toutes les autres routes) ...
@app.route('/api/docs/')
def serve_swagger_ui():
""" Sert la documentation interactive Swagger UI. """
return render_template('swagger_ui.html')
Mise en place de l'ensemble
La structure finale de votre projet devrait ressembler à ceci :
pyswagger-tutorial/
├── app.py
├── generate_spec.py
├── requirements.txt
├── static/
│ └── openapi.json
├── templates/
│ └── swagger_ui.html
└── venv/
Maintenant pour le résultat final. Assurez-vous que votre application Flask est en cours d'exécution (python app.py
). Ouvrez votre navigateur web et naviguez vers :
http://127.0.0.1:5000/api/docs/
Vous devriez être accueilli par une belle page de documentation interactive pour votre API. Vous pouvez développer chaque point de terminaison, voir les schémas que vous avez définis, et même utiliser le bouton "Try it out" (Essayer) pour envoyer des requêtes en direct à votre application Flask en cours d'exécution directement depuis le navigateur.
Conclusion
Dans ce tutoriel, nous sommes passés d'un répertoire vide à une API entièrement documentée. Nous avons utilisé avec succès pyswagger
pour construire par programmation une spécification OpenAPI 3.0 détaillée en mappant son modèle objet à la structure de notre API. Nous avons vu comment définir les métadonnées, les serveurs, les schémas réutilisables, les paths et les opérations avec leurs paramètres et réponses.
En séparant la génération de la spécification (generate_spec.py
) de la logique de l'application (app.py
), nous avons créé un flux de travail propre : implémentez votre API, puis exécutez le générateur pour produire la documentation. Ce processus, bien que plus manuel que les approches basées sur des décorateurs, offre un contrôle et une flexibilité inégalés, ce qui le rend idéal pour les projets complexes, les bases de code héritées, ou lorsque les normes de documentation sont strictes.
Enfin, en servant la spécification générée et une instance Swagger UI, nous avons fourni un portail de documentation soigné, professionnel et très utilisable pour les consommateurs de notre API. Vous disposez maintenant d'un nouvel outil puissant dans votre arsenal de développement Python pour créer une documentation API de classe mondiale.
Vous voulez une plateforme intégrée, Tout-en-Un pour que votre équipe de développeurs travaille ensemble avec une productivité maximale ?
Apidog répond à toutes vos demandes et remplace Postman à un prix beaucoup plus abordable !