يُعد إنشاء وثائق واجهة برمجة التطبيقات (API) شاملة ودقيقة جزءًا حيويًا ولكنه غالبًا ما يكون مملًا من تطوير البرمجيات. ظهرت مواصفات OpenAPI (المعروفة سابقًا باسم Swagger) كمعيار صناعي لتحديد واجهات برمجة التطبيقات RESTful. توفر تنسيقًا قابلًا للقراءة آليًا يسمح لكل من البشر وأجهزة الكمبيوتر باكتشاف وفهم قدرات الخدمة دون الوصول إلى الكود المصدري أو الوثائق أو من خلال فحص حركة مرور الشبكة.1
بينما توفر العديد من الأطر عمل إضافات لإنشاء مواصفات OpenAPI من تعليقات الكود (مثل docstrings)، هناك سيناريوهات قد تحتاج فيها إلى تحكم برمجي مباشر أكثر في إنشاء المواصفات. قد يكون هذا بسبب عملك مع نظام قديم، أو إطار عمل غير قياسي، أو تحتاج إلى إنشاء مواصفات لواجهة برمجة تطبيقات تتكون من خدمات مصغرة متعددة.
هنا يأتي دور pyswagger
. إنها مكتبة Python قوية تعمل كأداة لمواصفات OpenAPI. بينما تُستخدم غالبًا كعميل API لاستهلاك الخدمات المحددة بواسطة مواصفات OpenAPI، فإن قوتها الحقيقية تكمن في نموذج الكائن الخاص بها، والذي يسمح لك بإنشاء المواصفات والتلاعب بها والتحقق منها برمجيًا.
في هذا البرنامج التعليمي الشامل، سنتناول عملية استخدام pyswagger
لإنشاء مواصفات OpenAPI 3.0 كاملة، يدويًا ولكن تلقائيًا، لتطبيق ويب بسيط بلغة Python مبني باستخدام Flask. سنبني المواصفات من الألف إلى الياء، جزءًا تلو الآخر، موضحين كيف تتوافق كائنات pyswagger
مباشرة مع مكونات معيار OpenAPI. في النهاية، لن يكون لديك ملف openapi.json
مُنشأ فحسب، بل أيضًا واجهة مستخدم تفاعلية حية للوثائق تُقدم مباشرة من تطبيقك.
هل تريد منصة متكاملة وشاملة لفريق المطورين الخاص بك للعمل معًا بأقصى إنتاجية؟
Apidog يلبي جميع متطلباتك، ويحل محل Postman بسعر معقول جدًا!
الجزء الأول: إعداد بيئة المشروع
قبل أن نبدأ في إنشاء مواصفاتنا، نحتاج إلى إعداد بيئة تطوير مناسبة. يتضمن ذلك إنشاء بيئة Python معزولة لإدارة تبعياتنا وتثبيت المكتبات الضرورية.
إنشاء مساحة عملك ⚙️
أولاً، لنقم بإنشاء دليل لمشروعنا. افتح طرفيتك أو موجه الأوامر وقم بتشغيل الأوامر التالية: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
البيئة الافتراضية هي شجرة دليل ذاتية الاحتواء تتضمن تثبيت Python وعددًا من الملفات الداعمة. يضمن استخدام بيئة افتراضية أن الحزم التي نثبتها لهذا المشروع لا تتعارض مع الحزم المثبتة لمشاريع أخرى.
الآن، قم بتنشيط البيئة الافتراضية:Bash
# On macOS/Linux
source venv/bin/activate
# On Windows
.\venv\Scripts\activate
بمجرد التنشيط، يجب أن يتغير موجه الطرفية ليظهر اسم البيئة الافتراضية (على سبيل المثال، (venv)
)، مما يشير إلى أنك تعمل الآن داخلها.
تثبيت المكتبات الضرورية
مع تفعيل بيئتنا، يمكننا تثبيت مكتبات Python التي سنحتاجها لهذا البرنامج التعليمي. نحتاج إلى pyswagger
نفسه لبناء المواصفات، وFlask
لإنشاء واجهة برمجة تطبيقات الويب البسيطة الخاصة بنا، وPyYAML
لأن pyswagger
يستخدمها لعمليات YAML.Bash
pip install "pyswagger[utils]" Flask PyYAML
الجزء [utils]
عند تثبيت pyswagger
هو ممارسة جيدة لأنه يتضمن أدوات مساعدة مفيدة، مثل أداة التحقق التي سنستخدمها لاحقًا للتحقق من صحة المواصفات التي أنشأناها.
لإدارة مشروع جيدة، من الحكمة تثبيت تبعياتك في ملف requirements.txt
.Bash
pip freeze > requirements.txt
سيحتوي ملف requirements.txt
الآن على المكتبات وإصداراتها المحددة، مما يجعل مشروعك قابلاً للتكرار بسهولة من قبل الآخرين.
إنشاء تطبيق Flask الأساسي
الآن، لنقم بإنشاء تطبيق Flask بسيط للغاية. سيكون هذا بمثابة الأساس لواجهة برمجة التطبيقات التي سنقوم بتوثيقها.
في دليل مشروعك، قم بإنشاء ملف جديد باسم app.py
وأضف الكود التالي: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)
يقوم هذا الكود بإعداد خادم ويب بسيط للغاية بنقطة نهاية واحدة. لتشغيله، تأكد من أن بيئتك الافتراضية لا تزال نشطة وقم بتنفيذ الأمر التالي في طرفيتك:Bash
python app.py
يجب أن ترى مخرجات تشير إلى أن الخادم قيد التشغيل، شيء مثل هذا:
* Serving Flask app 'app'
* Debug mode: on
* Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
يمكنك الآن فتح متصفح الويب الخاص بك أو استخدام أداة مثل curl
لزيارة http://127.0.0.1:5000
. يجب أن ترى استجابة JSON: {"message": "API is up and running!"}
.
مع إعداد بيئتنا الأساسية وهيكل التطبيق، يمكننا الآن التعمق في المفاهيم الأساسية لـ pyswagger
.
الجزء الثاني: فهم نموذج كائن pyswagger
لاستخدام pyswagger
بفعالية لإنشاء مواصفات، تحتاج أولاً إلى فهم كيفية توافق نموذج الكائن الخاص بها مع هيكل مستند OpenAPI. مواصفات OpenAPI هي في الأساس كائن JSON أو YAML كبير ذو مخطط محدد. يوفر pyswagger
فئات وكائنات Python تعكس هذا المخطط، مما يسمح لك ببناء المواصفات بطريقة أكثر سهولة وموجهة نحو الكائنات.
المفاهيم الأساسية لمواصفات OpenAPI 3.0 📜
يحتوي مستند OpenAPI 3.0 على بعض الحقول الرئيسية على المستوى الأعلى:
openapi
: سلسلة تحدد إصدار مواصفات OpenAPI (على سبيل المثال،'3.0.0'
).info
: كائن يوفر بيانات وصفية حول واجهة برمجة التطبيقات. يتضمن ذلكtitle
،version
،description
، ومعلومات الاتصال.servers
: مصفوفة من كائنات الخادم، والتي تحدد عناوين URL الأساسية لواجهة برمجة التطبيقات.paths
: أهم حقل. يحتوي هذا الكائن على جميع نقاط نهاية واجهة برمجة التطبيقات المتاحة (paths) وعمليات HTTP (GET، POST، PUT، DELETE، إلخ) التي يمكن إجراؤها عليها.components
: كائن يحتوي على مجموعة من الكائنات القابلة لإعادة الاستخدام لأجزاء مختلفة من المواصفات. هذا هو المفتاح للحفاظ على مواصفاتك DRY (لا تكرر نفسك). يمكنك تعريفschemas
قابلة لإعادة الاستخدام (نماذج البيانات)،responses
،parameters
،examples
، والمزيد.
ربط OpenAPI بكائنات pyswagger
يوفر pyswagger
ربطًا نظيفًا من مفاهيم OpenAPI هذه إلى كائنات Python. دعنا نستكشف الكائنات الأساسية التي سنستخدمها.
الكائن المركزي في pyswagger
هو App
. يمكنك اعتبار مثيل App
هو جذر مستند OpenAPI الخاص بك.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')
بمجرد الحصول على كائن App
الخاص بك، يمكنك البدء في ملء سماته، والتي تتوافق مباشرة مع حقول OpenAPI. يستخدم pyswagger
نمط بناء، مما يسمح ببناء جملة سلسة وقابلة للقراءة.
المعلومات (Info) والخوادم (Servers)
قسمي info
وservers
سهلان في الملء.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)
المسارات (Paths) والعمليات (Operations)
يتم تعريف المسارات على كائن App
. تقوم بإضافة مسارات جديدة ثم تحديد العمليات (طرق HTTP) داخلها. يتم تكوين كل عملية بتفاصيل مثل summary
، description
، parameters
، requestBody
، وresponses
.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"
المكونات (Components): المخططات (Schemas)، المعلمات (Parameters)، والاستجابات (Responses)
القوة الحقيقية لمواصفات OpenAPI جيدة التنظيم تأتي من المكونات القابلة لإعادة الاستخدام. بدلاً من تعريف هيكل كائن "مستخدم" في كل مرة يظهر فيها في استجابة، تقوم بتعريفه مرة واحدة في components/schemas
ثم تشير إليه باستخدام مؤشر $ref
. يتعامل pyswagger
مع هذا بأناقة.
Schema
: كائن Schema
يحدد نموذج بيانات. يمكنك تحديد نوعه (object
، string
، integer
) وخصائصه.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
: كائن Parameter
يحدد معلمة عملية واحدة. تحدد اسمه، ومكان وجوده (in
: 'path'
، 'query'
، 'header'
، أو 'cookie'
)، ومخططه.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
: كائن Response
يحدد هيكل الاستجابة لرمز حالة HTTP محدد. يتضمن description
وcontent
، الذي يحدد نوع الوسائط (مثل application/json
) ومخططه.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'}
}
}
})
فهم هذا الربط هو المفتاح لبناء مواصفاتك. أنت في الأساس تقوم ببناء رسم بياني لكائن Python سيقوم pyswagger
لاحقًا بتسلسله إلى ملف OpenAPI JSON أو YAML صالح.
الجزء الثالث: بناء واجهة برمجة تطبيقات بسيطة باستخدام Flask
لجعل تمرين التوثيق عمليًا، نحتاج إلى واجهة برمجة تطبيقات فعلية لتوثيقها. سنقوم بتوسيع تطبيق Flask البسيط الخاص بنا من الجزء الأول إلى واجهة برمجة تطبيقات REST بسيطة لإدارة قائمة بالمستخدمين. ستعمل واجهة برمجة التطبيقات هذه كـ "مصدر الحقيقة" الذي سنصفه باستخدام pyswagger
.
تصميم واجهة برمجة تطبيقات بسيطة "للمستخدمين" 📝
سنقوم بتنفيذ أربع نقاط نهاية أساسية تمثل عمليات CRUD (إنشاء، قراءة، تحديث، حذف) شائعة:
GET /users
: استرداد قائمة بجميع المستخدمين.POST /users
: إنشاء مستخدم جديد.GET /users/{user_id}
: الحصول على مستخدم واحد بواسطة معرفه.DELETE /users/{user_id}
: حذف مستخدم بواسطة معرفه.
للبساطة، سنستخدم قاموس Python بسيط كـ "قاعدة بيانات" في الذاكرة. في تطبيق حقيقي، سيكون هذا اتصالاً بقاعدة بيانات مثل PostgreSQL أو MongoDB.
تنفيذ نقاط نهاية Flask
دعنا نحدث ملف app.py
الخاص بنا لتضمين منطق نقاط النهاية هذه. استبدل محتوى app.py
بما يلي: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)
الآن، إذا قمت بتشغيل python app.py
مرة أخرى، سيكون لديك واجهة برمجة تطبيقات تعمل بكامل طاقتها (على الرغم من بساطتها). يمكنك اختبارها باستخدام curl
أو أداة مشابهة:
- الحصول على جميع المستخدمين:
curl http://127.0.0.1:5000/users
- الحصول على مستخدم محدد:
curl http://127.0.0.1:5000/users/1
- إنشاء مستخدم:
curl -X POST -H "Content-Type: application/json" -d '{"username": "david", "email": "david@example.com"}' http://127.0.0.1:5000/users
- حذف مستخدم:
curl -X DELETE http://127.0.0.1:5000/users/3
مع تنفيذ واجهة برمجة التطبيقات الخاصة بنا، لدينا هدف ملموس لجهود التوثيق لدينا. الخطوة التالية هي استخدام pyswagger
لوصف كل نقطة من نقاط النهاية هذه بالتفصيل.
الجزء الرابع: الإنشاء التلقائي لمواصفات OpenAPI باستخدام pyswagger
هذا هو جوهر برنامجنا التعليمي. سنقوم الآن بإنشاء سكريبت Python منفصل يقوم باستيراد pyswagger
، وتحديد هيكل واجهة برمجة التطبيقات الخاصة بنا باستخدام نموذج الكائن الخاص بها، ثم تسلسل هذا الهيكل إلى ملف openapi.json
كامل. يفصل هذا النهج بين إنشاء المواصفات ومنطق التطبيق، والذي يمكن أن يكون نمطًا نظيفًا وقابلاً للصيانة للغاية.
إنشاء مولد المواصفات
في دليل مشروعك، قم بإنشاء ملف جديد باسم generate_spec.py
. سيكون هذا السكريبت مسؤولاً عن بناء وحفظ مواصفات OpenAPI الخاصة بنا.
بناء المواصفات، خطوة بخطوة
دعنا نبني سكريبت generate_spec.py
جزءًا تلو الآخر.
1. الاستيرادات وتهيئة App
أولاً، نحتاج إلى استيراد كائن App
من pyswagger
وPyYAML
للمساعدة في تفريغ الملف النهائي. سنقوم بإنشاء كائن App
الجذر الخاص بنا وملء قسمي info
وservers
الأساسيين، تمامًا كما ناقشنا في الجزء الثاني.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. تحديد المكونات القابلة لإعادة الاستخدام (المخططات)
تصميم واجهة برمجة تطبيقات جيد يتجنب التكرار. سنقوم بتعريف نموذج بيانات User
الخاص بنا مرة واحدة ونعيد استخدامه. سنقوم أيضًا بتعريف مخطط Error
عام لاستجابات الأخطاء لدينا (مثل 404 Not Found).
أضف الكود التالي إلى generate_spec.py
: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. توثيق نقاط نهاية /users
الآن سنقوم بتعريف المسار /users
وعمليتيه: GET
وPOST
.
- بالنسبة لـ
GET
، تكون الاستجابة عبارة عن مصفوفة من كائناتUser
. - بالنسبة لـ
POST
، سيتوقع جسم الطلب كائنNewUser
، وستكون الاستجابة الناجحة كائنUser
واحد (بما في ذلك المعرف الجديد).
<!-- 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')
لاحظ استخدام set_ref
وA
(التي تعني "access") لبناء هيكل الكائن المتداخل بصيغة أكثر إيجازًا.
4. توثيق نقاط نهاية /users/{user_id}
بعد ذلك، سنقوم بتوثيق المسار للتفاعل مع مستخدم واحد. يتضمن هذا المسار معلمة مسار، {user_id}
.
- بالنسبة لـ
GET
، سنحتاج إلى تعريف معلمة المسار هذه. الاستجابة هي كائنUser
واحد أو خطأ404
. - بالنسبة لـ
DELETE
، نحتاج أيضًا إلى معلمة المسار. الاستجابة الناجحة هي204 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. التحقق من صحة المواصفات وحفظها
أخيرًا، الخطوة الأكثر إرضاءً. سنطلب من pyswagger
التحقق من صحة الرسم البياني للكائن الذي أنشأناه مقابل مخطط OpenAPI 3.0. إذا كان صالحًا، فسنقوم بتفريغه إلى ملف JSON.
أضف هذه الكتلة النهائية من الكود إلى generate_spec.py
: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}")
ملف generate_spec.py
الخاص بك الآن مكتمل. قم بتشغيله من طرفيتك:Bash
python generate_spec.py
إذا كان كل شيء صحيحًا، سترى المخرجات التالية:
Specification is valid.
Successfully generated openapi.json
سيكون لديك الآن ملف جديد، openapi.json
، في دليل مشروعك. افتحه واستكشف محتوياته. سترى مستند OpenAPI 3.0 منظمًا تمامًا يصف واجهة برمجة تطبيقات Flask الخاصة بك بتفاصيل دقيقة.
الجزء الخامس: تقديم الوثائق
وجود ملف openapi.json
أمر رائع للآلات ولإنشاء حزم SDK للعملاء، ولكن بالنسبة للمطورين البشر، فإن واجهة المستخدم التفاعلية أكثر فائدة بكثير. في هذا الجزء الأخير، سنقوم بدمج المواصفات التي أنشأناها في تطبيق Flask الخاص بنا وتقديمها باستخدام Swagger UI الشهير.
دمج المواصفات في Flask
أولاً، نحتاج إلى إنشاء نقطة نهاية في تطبيق Flask الخاص بنا تقدم ملف openapi.json
. يتيح ذلك لأدوات التوثيق جلب المواصفات مباشرة من واجهة برمجة التطبيقات قيد التشغيل.
عدّل app.py
لإضافة مسار جديد: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')
لكي يعمل هذا، يحتاج Flask إلى معرفة مكان العثور على الملفات الثابتة. بشكل افتراضي، يبحث في دليل يسمى static
. دعنا ننشئ واحدًا وننقل ملف openapi.json
الخاص بنا إليه.Bash
mkdir static
mv openapi.json static/
الآن، قم بتشغيل python app.py
، وانتقل إلى http://127.0.0.1:5000/api/docs/openapi.json
. يجب أن ترى JSON الخام لمواصفاتك.
تقديم واجهة مستخدم تفاعلية باستخدام Swagger UI 🎨
Swagger UI هو مجموعة من أصول HTML2 وJavaScript وCSS خالية من التبعيات تقوم بإنشاء وثائق جميلة ديناميكيًا من واجهة برمجة تطبيقات متوافقة مع OpenAPI. يمكننا تقديمها بسهولة من تطبيق Flask الخاص بنا.
إنشاء دليل القوالب (Templates): يستخدم Flask دليلًا يسمى templates
للبحث عن قوالب HTML.Bash
mkdir templates
إنشاء قالب Swagger UI: داخل دليل templates
، قم بإنشاء ملف جديد يسمى swagger_ui.html
. الصق كود HTML التالي فيه. يقوم هذا الكود بتحميل أصول Swagger UI من شبكة توصيل محتوى (CDN) عامة ويقوم بتكوينها لتحميل ملف المواصفات الخاص بنا.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>
```
إضافة مسار Flask: أخيرًا، أضف مسارًا إلى app.py
لعرض قالب HTML هذا. ستحتاج إلى استيراد render_template
من Flask.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')
وضع كل شيء معًا
يجب أن يبدو هيكل مشروعك النهائي كما يلي:
pyswagger-tutorial/
├── app.py
├── generate_spec.py
├── requirements.txt
├── static/
│ └── openapi.json
├── templates/
│ └── swagger_ui.html
└── venv/
الآن للنتيجة النهائية. تأكد من أن تطبيق Flask الخاص بك قيد التشغيل (python app.py
). افتح متصفح الويب الخاص بك وانتقل إلى:
http://127.0.0.1:5000/api/docs/
يجب أن تستقبلك صفحة وثائق جميلة وتفاعلية لواجهة برمجة التطبيقات الخاصة بك. يمكنك توسيع كل نقطة نهاية، ورؤية المخططات التي حددتها، وحتى استخدام زر "Try it out" لإرسال طلبات حية إلى تطبيق Flask قيد التشغيل مباشرة من المتصفح.
الخلاصة
في هذا البرنامج التعليمي، انتقلنا من دليل فارغ إلى واجهة برمجة تطبيقات موثقة بالكامل. استخدمنا pyswagger
بنجاح لإنشاء مواصفات OpenAPI 3.0 مفصلة برمجيًا عن طريق ربط نموذج الكائن الخاص بها بهيكل واجهة برمجة التطبيقات الخاصة بنا. رأينا كيفية تحديد البيانات الوصفية، والخوادم، والمخططات القابلة لإعادة الاستخدام، والمسارات، والعمليات مع معلماتها واستجاباتها.
عن طريق فصل إنشاء المواصفات (generate_spec.py
) عن منطق التطبيق (app.py
)، أنشأنا سير عمل نظيفًا: قم بتنفيذ واجهة برمجة التطبيقات الخاصة بك، ثم قم بتشغيل المولد لإنتاج الوثائق. هذه العملية، على الرغم من أنها أكثر يدوية من الأساليب القائمة على الزخارف (decorators)، توفر تحكمًا ومرونة لا مثيل لهما، مما يجعلها مثالية للمشاريع المعقدة، أو قواعد الكود القديمة، أو عندما تكون معايير التوثيق صارمة.
أخيرًا، من خلال تقديم المواصفات التي تم إنشاؤها ومثيل Swagger UI، قدمنا بوابة توثيق مصقولة واحترافية وسهلة الاستخدام للغاية لمستهلكي واجهة برمجة التطبيقات الخاصة بنا. لديك الآن أداة جديدة قوية في ترسانة تطوير Python الخاصة بك لإنشاء وثائق واجهة برمجة تطبيقات عالمية المستوى.
هل تريد منصة متكاملة وشاملة لفريق المطورين الخاص بك للعمل معًا بأقصى إنتاجية؟
Apidog يلبي جميع متطلباتك، ويحل محل Postman بسعر معقول جدًا!