Python no Servidor: do CGI às Aplicações WSGI Modernas
A Common Gateway Interface (CGI) foi a primeira forma de gerar conteúdo dinâmico na web, nos anos 1990. Embora revolucionária na época, a CGI sofre de uma limitação importante: cada requisição inicia um novo processo Python, o que é ineficiente e inviável em escala. A WSGI (Web Server Gateway Interface) introduziu um modelo mais eficiente em 2003. Hoje, frameworks WSGI como Flask e servidores ASGI como FastAPI dominam o cenário. Este guia traça essa evolução e mostra como migrar da CGI para as tecnologias modernas.
1. CGI: As Origens
O que é CGI?
CGI é um padrão que define como um servidor web (Apache, Nginx) executa um programa externo (script CGI) para processar uma requisição HTTP e gerar uma resposta.
O fluxo CGI:
- O cliente envia uma requisição HTTP ao servidor web
- O servidor web inicia um novo processo para executar o script CGI
- O script lê a entrada (variáveis de ambiente, stdin)
- O script gera uma saída (headers HTTP + body)
- O servidor web devolve a resposta ao cliente
- O processo termina
Exemplo mínimo de CGI
Suponha um servidor web configurado para executar os scripts .cgi em /cgi-bin/.
#!/usr/bin/env python
# Arquivo : /cgi-bin/hello.cgi
import cgi
import cgitb
# Ativar os relatórios de erro 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 : o servidor web envia uma resposta HTML ao navegador
Formulário CGI com processamento
#!/usr/bin/env python
# Arquivo : /cgi-bin/form_handler.cgi
import cgi
import html
# Fazer o parsing dos dados do formulário
form = cgi.FieldStorage()
nom = form.getvalue("nom", "Visiteur inconnu")
# Escapar o conteúdo para evitar injeções 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>")
# Requisição : GET /form_handler.cgi?nom=Alice
# Resposta : <h1>Bienvenue, Alice !</h1>
Limitações da CGI
| Limitação | Impacto |
|---|---|
| Novo processo por requisição | Custo de inicialização enorme; inviável em escala |
Módulo cgi descontinuado e depois removido | Depreciado desde o Python 3.11, com aviso; totalmente removido no Python 3.13 |
| Sem estado persistente | As conexões com o banco de dados são reabertas a cada requisição |
| Sem otimizações | Sem cache, sem pool de workers, sem concorrência |
| Segurança por conta do desenvolvedor | Sem proteção integrada contra injeções, CSRF, etc. |
2. WSGI: Um Modelo Melhor
O que é WSGI?
A WSGI (PEP 3333) é uma interface padronizada entre um servidor web e uma aplicação Python. Diferentemente da CGI, a aplicação permanece em memória entre as requisições, o que elimina o custo de inicialização.
Uma aplicação WSGI é uma função ou classe callable que aceita dois argumentos:
def application(environ, start_response):
"""
environ : dicionário contendo as informações da requisição HTTP
start_response : função para enviar os headers de resposta
Retorno : iterável de bytes (o body da resposta)
"""
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 : aplicação mais eficiente que a CGI
Exemplo de WSGI com formulário
from urllib.parse import parse_qs
import html
def app(environ, start_response):
"""Aplicação WSGI simples com processamento de formulário."""
method = environ["REQUEST_METHOD"]
path = environ.get("PATH_INFO", "/")
if method == "GET" and path == "/":
# Página inicial com formulário
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":
# Processar os dados do formulário
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"]
# Executar com wsgiref (servidor de desenvolvimento incluído)
# python -m wsgiref.simple_server --port 8000 app:app
# Acesse http://localhost:8000
Servir uma aplicação WSGI pura com wsgiref
# Arquivo : 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()
# Execução :
# python myapp.py
# Depois acesse http://localhost:8000
3. http.server para Prototipagem
O módulo http.server da stdlib oferece um servidor HTTP simples, útil para testar rapidamente um protótipo.
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 simples, mas sem cache, com tratamento de erros mínimo, etc.
4. CGI vs WSGI vs ASGI: Comparação
| Aspecto | CGI | WSGI | ASGI |
|---|---|---|---|
| Processos | Novo por requisição | Um processo persistente | Um/vários workers |
| Modelo | Síncrono (bash/Perl/Python) | Síncrono (Python) | Assíncrono (async/await) |
| Inicialização | Muito lenta (fork do SO) | Rápida (função em memória) | Rápida |
| Gerenciamento de estado | Impossível | Possível (cache, BD) | Possível |
| Escalabilidade | Ruim | Boa | Excelente |
| Frameworks | Nenhum (apenas cgi) | Flask, Django | FastAPI, Starlette |
| Exemplos de uso | Histórico (anos 1990) | Blogs, MVPs, aplicações clássicas | APIs em tempo real, WebSockets |
5. Migração de CGI para Flask
Veja como refatorar um script CGI simples para 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>")
Depois: 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 em JSON (moderno) em vez de HTML
return jsonify({"a": a, "b": b, "operation": operation, "result": result})
if __name__ == "__main__":
# pip install flask
# python app.py
# Acesse http://localhost:5000/calculate?a=10&b=5&op=add
app.run(debug=True, port=5000)
# Vantagens :
# - Sem parsing manual
# - Resposta JSON estruturada
# - Tratamento de erros integrado
# - Facilmente extensível (middleware, templates, BD)
6. Boas Práticas
- Abandone a CGI. O módulo
cgifoi removido no Python 3.13. Não há nenhuma razão para continuar usando-o. - Escolha um framework WSGI (Flask, Django) ou ASGI (FastAPI, Starlette). Não escreva WSGI puro em produção.
- Use um servidor de aplicação: Gunicorn (WSGI) ou Uvicorn (ASGI), não o servidor de desenvolvimento.
- Versione suas dependências:
pip freeze > requirements.txtou use Poetry/Pipenv. - Gerencie o estado com um banco de dados, não em memória. Os workers são stateless.
- Teste sua aplicação. Escreva testes unitários e de integração.
Conclusão
A CGI, embora histórica e elegante em sua época, tornou-se obsoleta. A WSGI trouxe eficiência graças a processos persistentes. Hoje, a ASGI e frameworks como FastAPI vão ainda mais longe com o assíncrono e o suporte nativo a WebSockets. Se você mantém código CGI legado, a migração para Flask ou FastAPI é uma etapa importante para manter a segurança, o desempenho e a manutenibilidade. Para novos projetos, comece diretamente com o FastAPI para aproveitar os padrões modernos e o desempenho ideal.
Sobre InSkillCoach
Especialista em treinamento e tecnologias
Coach especializado em tecnologias avançadas e IA, apoiado pela GNeurone Inc.
Certificações:
- AWS Certified Solutions Architect – Professional
- Certificações Google Cloud
- Microsoft Certified: DevOps Engineer Expert
- Certified Kubernetes Administrator (CKA)
- CompTIA Security+
Comentários
Os comentários são alimentados pelo GitHub Discussions
Conecte-se com o GitHub para participar da discussão