Die Generierung umfassender und präziser API-Dokumentation ist ein kritischer, aber oft mühsamer Teil der Softwareentwicklung. Die OpenAPI Specification (früher bekannt als Swagger) hat sich als Industriestandard für die Definition von RESTful APIs etabliert. Sie bietet ein maschinenlesbares Format, das es sowohl Menschen als auch Computern ermöglicht, die Fähigkeiten eines Dienstes zu entdecken und zu verstehen, ohne Zugriff auf Quellcode, Dokumentation oder durch Inspektion des Netzwerkverkehrs zu haben.1
Während viele Frameworks Plugins zur Generierung von OpenAPI-Spezifikationen aus Code-Annotationen (wie Docstrings) anbieten, gibt es Szenarien, in denen Sie eine direktere, programmatische Kontrolle über die Erstellung der Spezifikation benötigen. Dies könnte der Fall sein, wenn Sie mit einem Altsystem, einem nicht standardmäßigen Framework arbeiten oder eine Spezifikation für eine API generieren müssen, die aus mehreren Microservices besteht.
Hier kommt pyswagger
ins Spiel. Es ist eine leistungsstarke Python-Bibliothek, die als Toolkit für OpenAPI fungiert. Obwohl es oft als API-Client zur Nutzung von Diensten verwendet wird, die durch eine OpenAPI-Spezifikation definiert sind, liegt seine wahre Stärke in seinem Objektmodell, das es Ihnen ermöglicht, eine Spezifikation programmatisch zu konstruieren, zu manipulieren und zu validieren.
In diesem umfassenden Tutorial werden wir den Prozess der Verwendung von pyswagger
durchgehen, um manuell, aber dennoch automatisch, eine vollständige OpenAPI 3.0-Spezifikation für eine einfache Python-Webanwendung zu generieren, die mit Flask erstellt wurde. Wir werden die Spezifikation von Grund auf Stück für Stück aufbauen und dabei zeigen, wie die Objekte von pyswagger
direkt den Komponenten des OpenAPI-Standards zugeordnet sind. Am Ende werden Sie nicht nur eine generierte openapi.json
-Datei haben, sondern auch eine live, interaktive Dokumentations-UI, die direkt von Ihrer Anwendung bereitgestellt wird.
Möchten Sie eine integrierte All-in-One-Plattform, auf der Ihr Entwicklerteam mit maximaler Produktivität zusammenarbeiten kann?
Apidog erfüllt all Ihre Anforderungen und ersetzt Postman zu einem viel günstigeren Preis!
Teil 1: Einrichten der Projektumgebung
Bevor wir mit der Generierung unserer Spezifikation beginnen können, müssen wir eine geeignete Entwicklungsumgebung einrichten. Dazu gehört die Erstellung einer isolierten Python-Umgebung zur Verwaltung unserer Abhängigkeiten und die Installation der notwendigen Bibliotheken.
Erstellen Ihres Arbeitsbereichs ⚙️
Zuerst erstellen wir ein Verzeichnis für unser Projekt. Öffnen Sie Ihr Terminal oder Ihre Eingabeaufforderung und führen Sie die folgenden Befehle aus:Bash
# Create a new directory for our project
mkdir pyswagger-tutorial
cd pyswagger-tutorial
# Create a Python virtual environment
# On macOS/Linux
python3 -m venv venv
# On Windows
python -m venv venv
Eine virtuelle Umgebung ist ein in sich geschlossener Verzeichnisbaum, der eine Python-Installation und eine Reihe unterstützender Dateien enthält. Die Verwendung einer virtuellen Umgebung stellt sicher, dass die Pakete, die wir für dieses Projekt installieren, nicht mit Paketen kollidieren, die für andere Projekte installiert wurden.
Aktivieren Sie nun die virtuelle Umgebung:Bash
# On macOS/Linux
source venv/bin/activate
# On Windows
.\venv\Scripts\activate
Nach der Aktivierung sollte sich Ihre Terminaleingabeaufforderung ändern und den Namen der virtuellen Umgebung anzeigen (z. B. (venv)
), was darauf hinweist, dass Sie nun darin arbeiten.
Installieren notwendiger Bibliotheken
Nachdem unsere Umgebung aktiv ist, können wir die Python-Bibliotheken installieren, die wir für dieses Tutorial benötigen. Wir benötigen pyswagger
selbst, um die Spezifikation zu erstellen, Flask
, um unsere einfache Web-API zu erstellen, und PyYAML
, da pyswagger
es für YAML-Operationen verwendet.Bash
pip install "pyswagger[utils]" Flask PyYAML
Der Teil [utils]
bei der Installation von pyswagger
ist eine gute Praxis, da er hilfreiche Dienstprogramme enthält, wie z. B. einen Validator, den wir später verwenden werden, um unsere generierte Spezifikation auf Korrektheit zu überprüfen.
Für ein gutes Projektmanagement ist es ratsam, Ihre Abhängigkeiten in einer requirements.txt
-Datei festzuhalten.Bash
pip freeze > requirements.txt
Ihre requirements.txt
enthält nun die Bibliotheken und ihre spezifischen Versionen, wodurch Ihr Projekt von anderen leicht reproduzierbar wird.
Erstellen der grundlegenden Flask-Anwendung
Nun erstellen wir eine minimale Flask-Anwendung. Diese dient als Grundlage der API, die wir dokumentieren werden.
Erstellen Sie in Ihrem Projektverzeichnis eine neue Datei namens app.py
und fügen Sie den folgenden Code hinzu:Python
# app.py
from flask import Flask, jsonify
# Initialize the Flask application
app = Flask(__name__)
@app.route("/")
def index():
""" A simple endpoint to check if the app is running. """
return jsonify({"message": "API is up and running!"})
if __name__ == "__main__":
# Runs the Flask app on http://127.0.0.1:5000
app.run(debug=True)
Dieser Code richtet einen sehr einfachen Webserver mit einem einzigen Endpunkt ein. Um ihn auszuführen, stellen Sie sicher, dass Ihre virtuelle Umgebung noch aktiv ist, und führen Sie den folgenden Befehl in Ihrem Terminal aus:Bash
python app.py
Sie sollten eine Ausgabe sehen, die anzeigt, dass der Server läuft, etwa so:
* Serving Flask app 'app'
* Debug mode: on
* Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
Sie können nun Ihren Webbrowser öffnen oder ein Werkzeug wie curl
verwenden, um http://127.0.0.1:5000
zu besuchen. Sie sollten die JSON-Antwort sehen: {"message": "API is up and running!"}
.
Mit unserer grundlegenden Umgebung und dem Anwendungsskelett können wir nun in die Kernkonzepte von pyswagger
eintauchen.
Teil 2: Verstehen des pyswagger
-Objektmodells
Um pyswagger
effektiv zur Generierung einer Spezifikation nutzen zu können, müssen Sie zunächst verstehen, wie sein Objektmodell der Struktur eines OpenAPI-Dokuments entspricht. Eine OpenAPI-Spezifikation ist im Wesentlichen ein großes JSON- oder YAML-Objekt mit einem spezifischen Schema. pyswagger
bietet Python-Klassen und -Objekte, die dieses Schema widerspiegeln und Ihnen ermöglichen, die Spezifikation auf intuitivere, objektorientierte Weise zu erstellen.
Kernkonzepte der OpenAPI 3.0 Spezifikation 📜
Ein OpenAPI 3.0 Dokument hat einige wichtige Top-Level-Felder:
openapi
: Eine Zeichenkette, die die OpenAPI Spezifikationsversion angibt (z. B.'3.0.0'
).info
: Ein Objekt, das Metadaten zur API bereitstellt. Dazu gehörentitle
,version
,description
und Kontaktinformationen.servers
: Ein Array von Server-Objekten, die die Basis-URLs für die API definieren.paths
: Das wichtigste Feld. Dieses Objekt enthält alle verfügbaren API-Endpunkte (Pfade) und die HTTP-Operationen (GET, POST, PUT, DELETE usw.), die darauf ausgeführt werden können.components
: Ein Objekt, das eine Reihe wiederverwendbarer Objekte für verschiedene Teile der Spezifikation enthält. Dies ist entscheidend, um Ihre Spezifikation DRY (Don't Repeat Yourself) zu halten. Sie können wiederverwendbareschemas
(Datenmodelle),responses
,parameters
,examples
und mehr definieren.
Mapping von OpenAPI auf pyswagger
-Objekte
pyswagger
bietet eine saubere Zuordnung dieser OpenAPI-Konzepte zu Python-Objekten. Betrachten wir die wichtigsten, die wir verwenden werden.
Das zentrale Objekt in pyswagger
ist die App
. Sie können sich eine App
-Instanz als Wurzel Ihres OpenAPI-Dokuments vorstellen.Python
from pyswagger import App
# The root of the specification document
# We initialize it with a version, but it can also load from a URL or file
root_app = App(version='3.0.0')
Sobald Sie Ihr App
-Objekt haben, können Sie beginnen, seine Attribute zu füllen, die direkt den OpenAPI-Feldern entsprechen. pyswagger
verwendet ein Builder-Muster, das eine flüssige und lesbare Syntax ermöglicht.
Info und Server
Die Abschnitte info
und servers
sind einfach zu füllen.Python
# Populating the 'info' object
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."
# Populating the 'servers' array
# You create a Server object and append it
server = root_app.prepare_obj('Server', {'url': 'http://127.0.0.1:5000', 'description': 'Local development server'})
root_app.servers.append(server)
Pfade und Operationen
Pfade werden auf dem App
-Objekt definiert. Sie fügen neue Pfade hinzu und definieren dann die Operationen (HTTP-Methoden) darin. Jede Operation wird mit Details wie summary
, description
, parameters
, einem requestBody
und responses
konfiguriert.Python
# Defining a path and an operation
# This doesn't execute anything; it just builds the object structure.
path_item = root_app.define_path('/users')
get_op = path_item.define_op('get')
get_op.summary = "Retrieve a list of all users"
Komponenten: Schemata, Parameter und Antworten
Die wahre Stärke einer gut strukturierten OpenAPI-Spezifikation kommt von wiederverwendbaren Komponenten. Anstatt die Struktur eines "User"-Objekts jedes Mal neu zu definieren, wenn es in einer Antwort erscheint, definieren Sie es einmal in components/schemas
und verweisen dann mit einem $ref
-Pointer darauf. pyswagger
handhabt dies elegant.
Schema
: Ein Schema
-Objekt definiert ein Datenmodell. Sie können seinen Typ (object
, string
, integer
) und seine Eigenschaften angeben.Python
# Preparing a Schema object for a User
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']
})
# Add it to the reusable components
root_app.components.schemas['User'] = user_schema
Parameter
: Ein Parameter
-Objekt definiert einen einzelnen Operationsparameter. Sie geben seinen Namen an, wo er sich befindet (in
: 'path'
, 'query'
, 'header'
oder 'cookie'
), und sein Schema.Python
# Preparing a Parameter object for a user ID in the path
user_id_param = root_app.prepare_obj('Parameter', {
'name': 'user_id',
'in': 'path',
'description': 'ID of the user to retrieve',
'required': True,
'schema': {'type': 'integer'}
})
Response
: Ein Response
-Objekt definiert die Struktur einer Antwort für einen bestimmten HTTP-Statuscode. Es enthält eine description
und den content
, der den Medientyp (z. B. application/json
) und sein Schema angibt.Python
# Preparing a Response object for a 200 OK response returning a single user
# Note the use of '$ref' to point to our reusable User schema
ok_user_response = root_app.prepare_obj('Response', {
'description': 'Successful retrieval of a user',
'content': {
'application/json': {
'schema': {'$ref': '#/components/schemas/User'}
}
}
})
Das Verständnis dieser Zuordnung ist der Schlüssel zum Aufbau Ihrer Spezifikation. Sie konstruieren im Wesentlichen einen Python-Objektgraphen, den pyswagger
später in eine gültige OpenAPI JSON- oder YAML-Datei serialisieren wird.
Teil 3: Erstellen einer einfachen API mit Flask
Um unsere Dokumentationsübung praktisch zu gestalten, benötigen wir eine tatsächliche API zum Dokumentieren. Wir werden unsere einfache Flask-App aus Teil 1 zu einer minimalen REST-API zur Verwaltung einer Liste von Benutzern erweitern. Diese API dient als "Quelle der Wahrheit", die wir mit pyswagger
beschreiben werden.
Entwerfen einer einfachen "User"-API 📝
Wir werden vier grundlegende Endpunkte implementieren, die gängige CRUD-Operationen (Create, Read, Update, Delete) darstellen:
GET /users
: Abrufen einer Liste aller Benutzer.POST /users
: Erstellen eines neuen Benutzers.GET /users/{user_id}
: Abrufen eines einzelnen Benutzers anhand seiner ID.DELETE /users/{user_id}
: Löschen eines Benutzers anhand seiner ID.
Der Einfachheit halber verwenden wir ein einfaches Python-Wörterbuch als unsere In-Memory-"Datenbank". In einer realen Anwendung wäre dies eine Verbindung zu einer Datenbank wie PostgreSQL oder MongoDB.
Implementieren der Flask-Endpunkte
Aktualisieren wir unsere Datei app.py
, um die Logik für diese Endpunkte aufzunehmen. Ersetzen Sie den Inhalt von app.py
durch Folgendes:Python
# app.py
from flask import Flask, jsonify, request, abort
app = Flask(__name__)
# --- In-Memory Database ---
# A simple dictionary to store our users.
# The key is the user_id (integer), and the value is the user data (dict).
USERS_DB = {
1: {"username": "alice", "email": "alice@example.com"},
2: {"username": "bob", "email": "bob@example.com"},
3: {"username": "charlie", "email": "charlie@example.com"},
}
# A counter to simulate auto-incrementing IDs for new users
LAST_INSERT_ID = 3
# --- API Endpoints ---
@app.route("/users", methods=["GET"])
def get_users():
""" Returns a list of all users. """
# We need to convert the dictionary to a list of user objects, including their IDs.
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():
""" Creates a new user. """
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
# The response should include the ID of the newly created user
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):
""" Returns a single user by their 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):
""" Deletes a user by their ID. """
if user_id not in USERS_DB:
abort(404, description=f"User with ID {user_id} not found.")
del USERS_DB[user_id]
# A 204 No Content response is standard for successful deletions
return '', 204
if __name__ == "__main__":
app.run(debug=True, port=5000)
Wenn Sie nun python app.py
erneut ausführen, haben Sie eine voll funktionsfähige (wenn auch einfache) API. Sie können sie mit curl
oder einem ähnlichen Werkzeug testen:
- Alle Benutzer abrufen:
curl http://127.0.0.1:5000/users
- Einen bestimmten Benutzer abrufen:
curl http://127.0.0.1:5000/users/1
- Einen Benutzer erstellen:
curl -X POST -H "Content-Type: application/json" -d '{"username": "david", "email": "david@example.com"}' http://127.0.0.1:5000/users
- Einen Benutzer löschen:
curl -X DELETE http://127.0.0.1:5000/users/3
Nachdem unsere API implementiert ist, haben wir ein konkretes Ziel für unsere Dokumentationsbemühungen. Der nächste Schritt ist die Verwendung von pyswagger
, um jeden dieser Endpunkte detailliert zu beschreiben.
Teil 4: Automatische Generierung der OpenAPI-Spezifikation mit pyswagger
Dies ist der Kern unseres Tutorials. Wir werden nun ein separates Python-Skript erstellen, das pyswagger
importiert, unsere API-Struktur mithilfe seines Objektmodells definiert und diese Struktur dann in eine vollständige openapi.json
-Datei serialisiert. Dieser Ansatz entkoppelt die Spezifikationsgenerierung von der Anwendungslogik, was ein sehr sauberer und wartbarer Ansatz sein kann.
Erstellen des Spezifikationsgenerators
Erstellen Sie in Ihrem Projektverzeichnis eine neue Datei namens generate_spec.py
. Dieses Skript ist für den Aufbau und das Speichern unserer OpenAPI-Spezifikation verantwortlich.
Schrittweiser Aufbau der Spezifikation
Bauen wir das Skript generate_spec.py
Stück für Stück auf.
1. Importe und Initialisierung der App
Zuerst müssen wir das App
-Objekt von pyswagger
und PyYAML
importieren, um beim Speichern der endgültigen Datei zu helfen. Wir erstellen unser Root-App
-Objekt und füllen die grundlegenden Abschnitte info
und servers
, genau wie wir es in Teil 2 besprochen haben.Python
# generate_spec.py
import json
from pyswagger import App
from pyswagger.contrib.client.requests import Client
# --- 1. Initialize the root App object ---
app = App(version='3.0.0')
# --- 2. Populating the Info & Servers sections ---
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': 'Local development server'
})
app.servers.append(server)
2. Definieren wiederverwendbarer Komponenten (Schemata)
Gutes API-Design vermeidet Wiederholungen. Wir definieren unser User
-Datenmodell einmal und verwenden es wieder. Wir definieren auch ein generisches Error
-Schema für unsere Fehlerantworten (wie 404 Not Found).
Fügen Sie den folgenden Code zu generate_spec.py
hinzu:Python
# --- 3. Defining Reusable Components (Schemas) ---
# Schema for the Error response
error_schema = app.prepare_obj('Schema', {
'type': 'object',
'properties': {
'code': {'type': 'integer', 'format': 'int32'},
'message': {'type': 'string'}
}
})
app.components.schemas['Error'] = error_schema
# Schema for a single User. Note the properties match our USERS_DB structure.
user_schema = app.prepare_obj('Schema', {
'type': 'object',
'properties': {
'id': {
'type': 'integer',
'description': 'Unique identifier for the user.',
'readOnly': True # The client cannot set this value
},
'username': {
'type': 'string',
'description': 'The user\'s chosen username.'
},
'email': {
'type': 'string',
'description': 'The user\'s email address.',
'format': 'email'
}
},
'required': ['id', 'username', 'email']
})
app.components.schemas['User'] = user_schema
# Schema for creating a user (doesn't include the 'id' field)
new_user_schema = app.prepare_obj('Schema', {
'type': 'object',
'properties': {
'username': {
'type': 'string',
'description': 'The user\'s chosen username.'
},
'email': {
'type': 'string',
'description': 'The user\'s email address.',
'format': 'email'
}
},
'required': ['username', 'email']
})
app.components.schemas['NewUser'] = new_user_schema
3. Dokumentieren der /users
-Endpunkte
Nun definieren wir den Pfad /users
und seine beiden Operationen: GET
und POST
.
- Für
GET
ist die Antwort ein Array vonUser
-Objekten. - Für
POST
erwartet der Anforderungsbody einNewUser
-Objekt, und die erfolgreiche Antwort ist ein einzelnesUser
-Objekt (einschließlich der neuen ID).
<!-- end list -->Python
# --- 4. Documenting the Paths ---
# -- Path: /users --
path_users = app.define_path('/users')
# Operation: GET /users
op_get_users = path_users.define_op('get')
op_get_users.summary = "List all users"
op_get_users.description = "Returns a JSON array of all user objects."
op_get_users.tags.append('Users')
op_get_users.responses.A('200').description = "A list of users."
op_get_users.responses.A('200').content.A('application/json').schema.A(
'array', items={'$ref': '#/components/schemas/User'}
)
# Operation: POST /users
op_post_users = path_users.define_op('post')
op_post_users.summary = "Create a new user"
op_post_users.description = "Adds a new user to the database."
op_post_users.tags.append('Users')
op_post_users.requestBody.description = "User object that needs to be added."
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 = "User created successfully."
op_post_users.responses.A('201').content.A('application/json').schema.set_ref('#/components/schemas/User')
op_post_users.responses.A('400').description = "Invalid input provided."
op_post_users.responses.A('400').content.A('application/json').schema.set_ref('#/components/schemas/Error')
Beachten Sie die Verwendung von set_ref
und A
(das für "access" steht) für eine prägnantere Syntax zum Aufbau der verschachtelten Objektstruktur.
4. Dokumentieren der /users/{user_id}
-Endpunkte
Als Nächstes dokumentieren wir den Pfad für die Interaktion mit einem einzelnen Benutzer. Dieser Pfad enthält einen Pfadparameter, {user_id}
.
- Für
GET
müssen wir diesen Pfadparameter definieren. Die Antwort ist ein einzelnesUser
-Objekt oder ein404
-Fehler. - Für
DELETE
benötigen wir ebenfalls den Pfadparameter. Die erfolgreiche Antwort ist ein204 No Content
.
<!-- end list -->Python
# -- Path: /users/{user_id} --
path_user_id = app.define_path('/users/{user_id}')
# We can define the parameter once and reuse it for all operations on this path.
user_id_param = app.prepare_obj('Parameter', {
'name': 'user_id',
'in': 'path',
'description': 'ID of the user',
'required': True,
'schema': {'type': 'integer'}
})
path_user_id.parameters.append(user_id_param)
# Operation: GET /users/{user_id}
op_get_user_id = path_user_id.define_op('get')
op_get_user_id.summary = "Find user by ID"
op_get_user_id.description = "Returns a single user."
op_get_user_id.tags.append('Users')
op_get_user_id.responses.A('200').description = "Successful operation."
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 = "User not found."
op_get_user_id.responses.A('404').content.A('application/json').schema.set_ref('#/components/schemas/Error')
# Operation: DELETE /users/{user_id}
op_delete_user_id = path_user_id.define_op('delete')
op_delete_user_id.summary = "Deletes a user"
op_delete_user_id.description = "Deletes a single user from the database."
op_delete_user_id.tags.append('Users')
op_delete_user_id.responses.A('204').description = "User deleted successfully."
op_delete_user_id.responses.A('404').description = "User not found."
op_delete_user_id.responses.A('404').content.A('application/json').schema.set_ref('#/components/schemas/Error')
5. Validieren und Speichern der Spezifikation
Schließlich der befriedigendste Schritt. Wir werden pyswagger
bitten, unseren konstruierten Objektgraphen anhand des OpenAPI 3.0 Schemas zu validieren. Wenn er gültig ist, speichern wir ihn in einer JSON-Datei.
Fügen Sie diesen letzten Codeblock zu generate_spec.py
hinzu:Python
# --- 5. Validate and Save the Specification ---
if __name__ == '__main__':
try:
# Validate the generated specification
app.validate()
print("Specification is valid.")
# Save the specification to a JSON file
with open('openapi.json', 'w') as f:
f.write(app.dump_json(indent=2))
print("Successfully generated openapi.json")
except Exception as e:
print(f"Validation Error: {e}")
Ihre Datei generate_spec.py
ist nun vollständig. Führen Sie sie von Ihrem Terminal aus:Bash
python generate_spec.py
Wenn alles korrekt ist, sehen Sie die Ausgabe:
Specification is valid.
Successfully generated openapi.json
Sie haben nun eine neue Datei, openapi.json
, in Ihrem Projektverzeichnis. Öffnen Sie sie und erkunden Sie ihren Inhalt. Sie sehen ein perfekt strukturiertes OpenAPI 3.0 Dokument, das Ihre Flask-API bis ins kleinste Detail beschreibt.
Teil 5: Bereitstellen der Dokumentation
Eine openapi.json
-Datei zu haben ist großartig für Maschinen und zur Generierung von Client-SDKs, aber für menschliche Entwickler ist eine interaktive Benutzeroberfläche weitaus nützlicher. In diesem letzten Teil werden wir unsere generierte Spezifikation in unsere Flask-App integrieren und sie mithilfe der beliebten Swagger UI bereitstellen.
Integrieren der Spezifikation in Flask
Zuerst müssen wir einen Endpunkt in unserer Flask-App erstellen, der die Datei openapi.json
bereitstellt. Dies ermöglicht es Dokumentationstools, die Spezifikation direkt von unserer laufenden API abzurufen.
Ändern Sie app.py
, um eine neue Route hinzuzufügen:Python
# app.py
# ... (keep all the existing Flask code) ...
import os
# ... (all the routes like /users, /users/{user_id}, etc.) ...
# --- Serving the OpenAPI Specification and UI ---
@app.route('/api/docs/openapi.json')
def serve_openapi_spec():
""" Serves the generated openapi.json file. """
# This assumes openapi.json is in the same directory as app.py
# In a larger app, you might want a more robust way to find the file
return app.send_static_file('openapi.json')
Damit dies funktioniert, muss Flask wissen, wo statische Dateien zu finden sind. Standardmäßig sucht es in einem Verzeichnis namens static
. Erstellen wir eines und verschieben unsere Datei openapi.json
dorthin.Bash
mkdir static
mv openapi.json static/
Führen Sie nun python app.py
aus und navigieren Sie zu http://127.0.0.1:5000/api/docs/openapi.json
. Sie sollten das rohe JSON Ihrer Spezifikation sehen.
Bereitstellen der interaktiven Benutzeroberfläche mit Swagger UI 🎨
Swagger UI ist eine sammlungsabhängigkeitsfreie Sammlung von HTML-,2 JavaScript- und CSS-Assets, die dynamisch schöne Dokumentation aus einer OpenAPI-kompatiblen API generiert. Wir können sie einfach von unserer Flask-Anwendung aus bereitstellen.
Erstellen eines Templates-Verzeichnisses: Flask verwendet ein Verzeichnis namens templates
, um nach HTML-Templates zu suchen.Bash
mkdir templates
Erstellen des Swagger UI Templates: Erstellen Sie im Verzeichnis templates
eine neue Datei namens swagger_ui.html
. Fügen Sie den folgenden HTML-Code ein. Dieser Code lädt die Swagger UI-Assets von einem öffentlichen CDN und konfiguriert sie, um unsere Spezifikationsdatei zu laden.HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>User API - 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() {
// Begin Swagger3 UI call region
const ui = SwaggerUIBundle({
url: "/api/docs/openapi.json", // The URL for our spec
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset4
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
// End Swagger UI call region
window.ui = ui
}
</script>
</body>
</html>
```
Hinzufügen der Flask-Route: Fügen Sie schließlich eine Route zu app.py
hinzu, um dieses HTML-Template zu rendern. Sie müssen render_template
von Flask importieren.Python
# At the top of app.py
from flask import Flask, jsonify, request, abort, render_template
# ... (all other routes) ...
@app.route('/api/docs/')
def serve_swagger_ui():
""" Serves the interactive Swagger UI documentation. """
return render_template('swagger_ui.html')
Alles zusammenfügen
Ihre endgültige Projektstruktur sollte so aussehen:
pyswagger-tutorial/
├── app.py
├── generate_spec.py
├── requirements.txt
├── static/
│ └── openapi.json
├── templates/
│ └── swagger_ui.html
└── venv/
Nun zum Endergebnis. Stellen Sie sicher, dass Ihre Flask-App läuft (python app.py
). Öffnen Sie Ihren Webbrowser und navigieren Sie zu:
http://127.0.0.1:5000/api/docs/
Sie sollten mit einer schönen, interaktiven Dokumentationsseite für Ihre API begrüßt werden. Sie können jeden Endpunkt erweitern, die von Ihnen definierten Schemata sehen und sogar die Schaltfläche "Try it out" verwenden, um Live-Anfragen direkt von Ihrem Browser an Ihre laufende Flask-Anwendung zu senden.
Fazit
In diesem Tutorial sind wir von einem leeren Verzeichnis zu einer vollständig dokumentierten API gereist. Wir haben pyswagger
erfolgreich verwendet, um programmatisch eine detaillierte OpenAPI 3.0 Spezifikation zu konstruieren, indem wir ihr Objektmodell der Struktur unserer API zugeordnet haben. Wir haben gelernt, wie man Metadaten, Server, wiederverwendbare Schemata, Pfade und Operationen mit ihren Parametern und Antworten definiert.
Durch die Trennung der Spezifikationsgenerierung (generate_spec.py
) von der Anwendungslogik (app.py
) haben wir einen sauberen Workflow geschaffen: Implementieren Sie Ihre API, führen Sie dann den Generator aus, um die Dokumentation zu erstellen. Dieser Prozess ist zwar manueller als dekoratorbasierte Ansätze, bietet aber unvergleichliche Kontrolle und Flexibilität, was ihn ideal für komplexe Projekte, Altsysteme oder bei strengen Dokumentationsstandards macht.
Schließlich haben wir durch die Bereitstellung der generierten Spezifikation und einer Swagger UI-Instanz ein poliertes, professionelles und sehr nützliches Dokumentationsportal für die Konsumenten unserer API bereitgestellt. Sie verfügen nun über ein leistungsstarkes neues Werkzeug in Ihrem Python-Entwicklungsarsenal zur Erstellung erstklassiger API-Dokumentation.
Möchten Sie eine integrierte All-in-One-Plattform, auf der Ihr Entwicklerteam mit maximaler Produktivität zusammenarbeiten kann?
Apidog erfüllt all Ihre Anforderungen und ersetzt Postman zu einem viel günstigeren Preis!