0%
بايثون على جانب الخادم: من CGI إلى تطبيقات WSGI الحديثة

بايثون على جانب الخادم: من CGI إلى تطبيقات WSGI الحديثة

اكتشف تطور تطوير الويب في بايثون: CGI وحدوده، ثم WSGI، والانتقال إلى Flask/FastAPI لبناء تطبيقات حديثة وعالية الأداء.

I

InSkillCoach

· دقيقة

بايثون على جانب الخادم: من CGI إلى تطبيقات WSGI الحديثة

كانت واجهة البوابة المشتركة (Common Gateway Interface أو CGI) أول وسيلة لتوليد محتوى ديناميكي على الويب في تسعينيات القرن الماضي. ورغم أنها كانت ثورية في زمنها، فإن CGI تعاني من قيود جوهرية: فكل طلب يُطلق عملية Python جديدة، وهذا غير فعّال ويستحيل معه التوسع. وقد قدّمت WSGI (Web Server Gateway Interface) نموذجًا أكثر كفاءة في عام 2003. أما اليوم، فتهيمن أطر WSGI مثل Flask وخوادم ASGI مثل FastAPI على المشهد. يتتبع هذا الدليل هذا التطور ويوضح لك كيفية الانتقال من CGI إلى التقنيات الحديثة.

1. CGI: البدايات

ما هي CGI؟

CGI هو معيار يحدد كيف يشغّل خادم الويب (Apache وNginx) برنامجًا خارجيًا (سكربت CGI) لمعالجة طلب HTTP وتوليد استجابة.

مسار عمل CGI:

  1. يرسل العميل طلب HTTP إلى خادم الويب
  2. يبدأ خادم الويب عملية جديدة لتنفيذ سكربت CGI
  3. يقرأ السكربت المدخلات (متغيرات البيئة، stdin)
  4. يولّد السكربت المخرجات (ترويسات HTTP + المحتوى)
  5. يعيد خادم الويب الاستجابة إلى العميل
  6. تنتهي العملية

مثال CGI minimal

لنفترض خادم ويب مهيّأ لتنفيذ سكربتات .cgi الموجودة في /cgi-bin/.

#!/usr/bin/env python
# Fichier : /cgi-bin/hello.cgi

import cgi
import cgitb

# Activer les rapports d'erreur CGI
cgitb.enable()

print("Content-Type: text/html; charset=utf-8")
print()

print("<html><body>")
print("<h1>Bonjour depuis CGI !</h1>")
print("</body></html>")

# Résultat : le serveur web envoie une réponse HTML au navigateur

نموذج CGI مع المعالجة

#!/usr/bin/env python
# Fichier : /cgi-bin/form_handler.cgi

import cgi
import html

# Parser les données du formulaire
form = cgi.FieldStorage()
nom = form.getvalue("nom", "Visiteur inconnu")

# Échapper le contenu pour éviter les injections XSS
nom_safe = html.escape(nom)

print("Content-Type: text/html; charset=utf-8")
print()

print("<html><body>")
print(f"<h1>Bienvenue, {nom_safe} !</h1>")
print("</body></html>")

# Requête : GET /form_handler.cgi?nom=Alice
# Réponse : <h1>Bienvenue, Alice !</h1>

قيود CGI

القيدالأثر
عملية جديدة لكل طلبتكلفة بدء تشغيل هائلة؛ يستحيل التوسع
وحدة cgi أصبحت متقادمة ثم أُزيلتمنذ Python 3.11 مع تحذير؛ وحُذفت نهائيًا في Python 3.13
لا حالة دائمةتُعاد فتح الاتصالات بقاعدة البيانات مع كل طلب
لا تحسيناتلا ذاكرة تخزين مؤقت، ولا مجمّع عمّال (workers)، ولا تزامن
الأمان على عاتق المطوّرلا حماية مدمجة ضد الحقن وCSRF وغيرها

2. WSGI: نموذج أفضل

ما هي WSGI؟

WSGI (PEP 3333) هي واجهة موحّدة بين خادم الويب وتطبيق Python. وعلى عكس CGI، يبقى التطبيق في الذاكرة بين الطلبات، مما يلغي تكلفة بدء التشغيل.

تطبيق WSGI هو دالة أو فئة قابلة للاستدعاء (callable) تقبل وسيطين:

def application(environ, start_response):
    """
    environ : dictionnaire contenant les infos de la requête HTTP
    start_response : fonction pour envoyer les headers de réponse
    
    Retour : itérable de bytes (le body de la réponse)
    """
    status = "200 OK"
    response_headers = [("Content-Type", "text/html; charset=utf-8")]
    start_response(status, response_headers)
    
    body = "<html><body><h1>Bonjour WSGI !</h1></body></html>"
    return [body.encode("utf-8")]

# Résultat : application plus efficace que CGI

مثال WSGI مع نموذج

from urllib.parse import parse_qs
import html

def app(environ, start_response):
    """Application WSGI simple avec traitement de formulaire."""
    
    method = environ["REQUEST_METHOD"]
    path = environ.get("PATH_INFO", "/")
    
    if method == "GET" and path == "/":
        # Page d'accueil avec formulaire
        body = """
        <html>
        <body>
            <h1>Entrez votre nom</h1>
            <form method="post" action="/submit">
                <input type="text" name="nom" required>
                <button type="submit">Envoyer</button>
            </form>
        </body>
        </html>
        """.encode("utf-8")
        
        start_response("200 OK", [("Content-Type", "text/html; charset=utf-8")])
        return [body]
    
    elif method == "POST" and path == "/submit":
        # Traiter les données du formulaire
        content_length = int(environ.get("CONTENT_LENGTH", 0))
        body = environ["wsgi.input"].read(content_length).decode("utf-8")
        data = parse_qs(body)
        nom = data.get("nom", ["Visiteur"])[0]
        nom_safe = html.escape(nom)
        
        response = f"<html><body><h1>Bonjour, {nom_safe} !</h1></body></html>".encode("utf-8")
        
        start_response("200 OK", [("Content-Type", "text/html; charset=utf-8")])
        return [response]
    
    else:
        start_response("404 Not Found", [("Content-Type", "text/plain")])
        return [b"Page non trouvée"]

# Exécuter avec wsgiref (serveur de développement inclus)
# python -m wsgiref.simple_server --port 8000 app:app
# Accédez à http://localhost:8000

تشغيل تطبيق WSGI خام باستخدام wsgiref

# Fichier : myapp.py

def app(environ, start_response):
    start_response("200 OK", [("Content-Type", "text/plain; charset=utf-8")])
    return [b"Hello from WSGI!"]

if __name__ == "__main__":
    from wsgiref.simple_server import make_server
    server = make_server("localhost", 8000, app)
    print("Serveur WSGI lancé sur http://localhost:8000")
    server.serve_forever()

# Exécution :
# python myapp.py
# Puis accédez à http://localhost:8000

3. http.server للنمذجة الأولية

توفّر وحدة http.server من المكتبة القياسية خادم HTTP بسيطًا، مفيدًا لاختبار نموذج أولي بسرعة.

from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class MonHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/":
            self.send_response(200)
            self.send_header("Content-Type", "text/html; charset=utf-8")
            self.end_headers()
            self.wfile.write(b"<h1>Accueil</h1>")
        elif self.path == "/api/info":
            self.send_response(200)
            self.send_header("Content-Type", "application/json")
            self.end_headers()
            data = {"status": "ok", "message": "Réponse JSON"}
            self.wfile.write(json.dumps(data).encode("utf-8"))
        else:
            self.send_response(404)
            self.end_headers()
            self.wfile.write(b"Not found")

if __name__ == "__main__":
    server = HTTPServer(("localhost", 8000), MonHandler)
    print("Serveur lancé sur http://localhost:8000")
    server.serve_forever()

# Résultat : serveur simple mais sans cache, gestion d'erreur minimale, etc.

4. CGI مقابل WSGI مقابل ASGI: مقارنة

الجانبCGIWSGIASGI
العملياتعملية جديدة لكل طلبعملية دائمة واحدةعامل واحد أو عدة عمّال
النموذجمتزامن (bash/Perl/Python)متزامن (Python)غير متزامن (async/await)
بدء التشغيلبطيء جدًا (fork في نظام التشغيل)سريع (دالة في الذاكرة)سريع
إدارة الحالةمستحيلةممكنة (ذاكرة تخزين مؤقت، قاعدة بيانات)ممكنة
قابلية التوسعضعيفةجيدةممتازة
الأطرلا شيء (cgi فقط)Flask, DjangoFastAPI, Starlette
أمثلة الاستخدامتاريخي (التسعينيات)المدونات، المنتجات الأولية القابلة للتطبيق (MVP)، التطبيقات الكلاسيكيةواجهات API الفورية، WebSockets

5. الانتقال من CGI إلى Flask

إليك كيفية إعادة هيكلة سكربت CGI بسيط إلى Flask حديث.

قبل: CGI

#!/usr/bin/env python
# cgi-bin/calculator.cgi

import cgi

form = cgi.FieldStorage()
a = float(form.getvalue("a", 0))
b = float(form.getvalue("b", 0))
operation = form.getvalue("op", "add")

if operation == "add":
    result = a + b
elif operation == "subtract":
    result = a - b
else:
    result = 0

print("Content-Type: text/html; charset=utf-8")
print()
print(f"<h1>Résultat : {result}</h1>")

بعد: Flask

# app.py

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/calculate", methods=["GET", "POST"])
def calculate():
    a = float(request.args.get("a", 0))
    b = float(request.args.get("b", 0))
    operation = request.args.get("op", "add")
    
    if operation == "add":
        result = a + b
    elif operation == "subtract":
        result = a - b
    else:
        result = 0
    
    # Répondre en JSON (moderne) au lieu d'HTML
    return jsonify({"a": a, "b": b, "operation": operation, "result": result})

if __name__ == "__main__":
    # pip install flask
    # python app.py
    # Accédez à http://localhost:5000/calculate?a=10&b=5&op=add
    app.run(debug=True, port=5000)

# Avantages :
# - Pas de parsing manuel
# - Réponse JSON structurée
# - Gestion d'erreur intégrée
# - Extensible facilement (middleware, templates, BD)

6. أفضل الممارسات

  • تخلَّ عن CGI. فقد أُزيلت وحدة cgi منذ Python 3.13. ولا يوجد أي سبب للبقاء عليها.
  • اختر إطار WSGI (Flask وDjango) أو ASGI (FastAPI وStarlette). لا تكتب WSGI خامًا في بيئة الإنتاج.
  • استخدم خادم تطبيقات: Gunicorn (لـ WSGI) أو Uvicorn (لـ ASGI)، وليس خادم التطوير.
  • ثبّت إصدارات تبعياتك: pip freeze > requirements.txt أو استخدم Poetry/Pipenv.
  • أدِر الحالة عبر قاعدة بيانات، وليس في الذاكرة. فالعمّال (workers) عديمو الحالة (stateless).
  • اختبر تطبيقك. اكتب اختبارات الوحدة واختبارات التكامل.

الخاتمة

أصبحت CGI، رغم أهميتها التاريخية وأناقتها في زمنها، قديمة. وقد جلبت WSGI الكفاءة بفضل العمليات الدائمة. أما اليوم، فتذهب ASGI وأطر مثل FastAPI إلى أبعد من ذلك بفضل البرمجة غير المتزامنة والدعم الأصلي لـ WebSockets. وإذا كنت تصون شيفرة CGI موروثة، فإن الانتقال إلى Flask أو FastAPI خطوة مهمة للحفاظ على الأمان والأداء وقابلية الصيانة. أما في المشاريع الجديدة، فابدأ مباشرة بـ FastAPI للاستفادة من المعايير الحديثة وأفضل أداء.

InSkillCoach

نبذة عن InSkillCoach

خبير في التدريب والتقنيات

مدرّب متخصص في التقنيات المتقدمة والذكاء الاصطناعي، تدعمه شركة GNeurone Inc.

الشهادات:

  • AWS Certified Solutions Architect – Professional
  • شهادات Google Cloud
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
1.3k
115

التعليقات

التعليقات مدعومة عبر GitHub Discussions

سجّل الدخول باستخدام GitHub للمشاركة في النقاش

تم نسخ الرابط!