0%
Python en el servidor: del CGI a las aplicaciones WSGI modernas

Python en el servidor: del CGI a las aplicaciones WSGI modernas

Explora la evolución del desarrollo web en Python: CGI, sus límites, WSGI y la transición hacia Flask/FastAPI para crear aplicaciones modernas y eficientes.

I

InSkillCoach

· min

Python en el servidor: del CGI a las aplicaciones WSGI modernas

La interfaz Common Gateway Interface (CGI) fue la primera forma de generar contenido dinámico en la web en los años 90. Aunque resultó revolucionaria en su momento, CGI sufre una limitación importante: cada petición lanza un nuevo proceso de Python, lo que es ineficiente e imposible de escalar. WSGI (Web Server Gateway Interface) introdujo en 2003 un modelo más eficiente. Hoy en día, los frameworks WSGI como Flask y los servidores ASGI como FastAPI dominan el panorama. Esta guía traza esa evolución y te muestra cómo migrar de CGI a las tecnologías modernas.

1. CGI: los orígenes

¿Qué es CGI?

CGI es un estándar que define cómo un servidor web (Apache, Nginx) ejecuta un programa externo (script CGI) para procesar una petición HTTP y generar una respuesta.

El flujo de CGI:

  1. El cliente envía una petición HTTP al servidor web
  2. El servidor web inicia un nuevo proceso para ejecutar el script CGI
  3. El script lee la entrada (variables de entorno, stdin)
  4. El script genera una salida (cabeceras HTTP + cuerpo)
  5. El servidor web devuelve la respuesta al cliente
  6. El proceso termina

Ejemplo mínimo de CGI

Supongamos un servidor web configurado para ejecutar los scripts .cgi que están en /cgi-bin/.

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

import cgi
import cgitb

# Activar los informes de error de CGI
cgitb.enable()

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

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

# Resultado: el servidor web envía una respuesta HTML al navegador

Formulario CGI con procesamiento

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

import cgi
import html

# Parsear los datos del formulario
form = cgi.FieldStorage()
nom = form.getvalue("nom", "Visiteur inconnu")

# Escapar el contenido para evitar inyecciones 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>")

# Petición: GET /form_handler.cgi?nom=Alice
# Respuesta: <h1>Bienvenue, Alice !</h1>

Limitaciones de CGI

LimitaciónImpacto
Nuevo proceso por peticiónCoste de arranque enorme; imposible de escalar
Módulo cgi obsoleto y luego eliminadoObsoleto desde Python 3.11, con advertencia; eliminado por completo en Python 3.13
Sin estado persistenteLas conexiones a la base de datos se vuelven a abrir en cada petición
Sin optimizacionesSin caché, sin pool de workers, sin concurrencia
Seguridad a cargo del desarrolladorSin protección integrada contra inyecciones, CSRF, etc.

2. WSGI: un modelo mejor

¿Qué es WSGI?

WSGI (PEP 3333) es una interfaz estandarizada entre un servidor web y una aplicación Python. A diferencia de CGI, la aplicación permanece en memoria entre peticiones, lo que elimina el coste de arranque.

Una aplicación WSGI es una función o clase invocable (callable) que acepta dos argumentos:

def application(environ, start_response):
    """
    environ: diccionario con la información de la petición HTTP
    start_response: función para enviar las cabeceras de la respuesta
    
    Retorno: iterable de bytes (el cuerpo de la respuesta)
    """
    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")]

# Resultado: aplicación más eficiente que CGI

Ejemplo de WSGI con formulario

from urllib.parse import parse_qs
import html

def app(environ, start_response):
    """Aplicación WSGI sencilla con procesamiento de formulario."""
    
    method = environ["REQUEST_METHOD"]
    path = environ.get("PATH_INFO", "/")
    
    if method == "GET" and path == "/":
        # Página de inicio con formulario
        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":
        # Procesar los datos del formulario
        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"]

# Ejecutar con wsgiref (servidor de desarrollo incluido)
# python -m wsgiref.simple_server --port 8000 app:app
# Accede a http://localhost:8000

Servir una aplicación WSGI pura con 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()

# Ejecución:
# python myapp.py
# Después accede a http://localhost:8000

3. http.server para prototipado

El módulo http.server de la biblioteca estándar ofrece un servidor HTTP sencillo, útil para probar rápidamente un prototipo.

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()

# Resultado: servidor sencillo, pero sin caché, con gestión de errores mínima, etc.

4. CGI vs WSGI vs ASGI: comparación

AspectoCGIWSGIASGI
ProcesosUno nuevo por peticiónUn proceso persistenteUno o varios workers
ModeloSíncrono (bash/Perl/Python)Síncrono (Python)Asíncrono (async/await)
ArranqueMuy lento (fork del SO)Rápido (función en memoria)Rápido
Gestión de estadoImposiblePosible (caché, BD)Posible
EscalabilidadMalaBuenaExcelente
FrameworksNinguno (solo cgi)Flask, DjangoFastAPI, Starlette
Ejemplos de usoHistórico (años 90)Blogs, MVP, aplicaciones clásicasAPIs en tiempo real, WebSockets

5. Migración de CGI a Flask

Veamos cómo refactorizar un script CGI sencillo a Flask moderno.

Antes: 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>")

Después: 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
    
    # Responder en JSON (moderno) en lugar de HTML
    return jsonify({"a": a, "b": b, "operation": operation, "result": result})

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

# Ventajas:
# - Sin parseo manual
# - Respuesta JSON estructurada
# - Gestión de errores integrada
# - Fácilmente extensible (middleware, plantillas, BD)

6. Buenas prácticas

  • Abandona CGI. El módulo cgi está eliminado desde Python 3.13. No hay ninguna razón para seguir usándolo.
  • Elige un framework WSGI (Flask, Django) o ASGI (FastAPI, Starlette). No programes WSGI puro en producción.
  • Utiliza un servidor de aplicaciones: Gunicorn (WSGI) o Uvicorn (ASGI), no el servidor de desarrollo.
  • Versiona tus dependencias: pip freeze > requirements.txt o utiliza Poetry/Pipenv.
  • Gestiona el estado con una base de datos, no en memoria. Los workers son stateless.
  • Prueba tu aplicación. Escribe tests unitarios y de integración.

Conclusión

CGI, aunque histórico y elegante en su época, ha quedado obsoleto. WSGI aportó eficiencia gracias a los procesos persistentes. Hoy, ASGI y frameworks como FastAPI van aún más lejos con la asincronía y el soporte nativo de WebSockets. Si mantienes código CGI heredado, migrar a Flask o FastAPI es un paso importante para mantener la seguridad, el rendimiento y la mantenibilidad. Para los proyectos nuevos, parte directamente de FastAPI para beneficiarte de los estándares modernos y de un rendimiento óptimo.

InSkillCoach

Acerca de InSkillCoach

Experto en formación y tecnologías

Coach especializado en tecnologías avanzadas e IA, impulsado por GNeurone Inc.

Certificaciones:

  • AWS Certified Solutions Architect – Professional
  • Certificaciones de Google Cloud
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
1.0k
76

Comentarios

Los comentarios funcionan con GitHub Discussions

Inicia sesión con GitHub para participar en la discusión

¡Enlace copiado!