wsgiref no Python: aplicações WSGI

Publicado em: 12/08/2026
Tempo de leitura: 5 minutos
Código de aplicação web representando WSGI com wsgiref no Python

O pacote wsgiref contém utilitários e uma implementação de referência da especificação WSGI, a interface tradicional entre servidores web e aplicações Python síncronas. Ele permite construir aplicações mínimas, testar o dicionário environ, manipular headers, validar conformidade e executar um servidor HTTP simples durante desenvolvimento.

A própria documentação alerta: wsgiref não é recomendado para produção e implementa apenas verificações básicas de segurança. Use-o para aprendizado, testes unitários, validação de middleware e protótipos locais. Em produção, escolha um servidor WSGI mantido, configurado para concorrência, TLS, timeouts, limites e observabilidade.

Como funciona uma aplicação WSGI

Uma aplicação WSGI é um callable que recebe environ e start_response. Ela deve chamar start_response() com status e headers e devolver um iterável de bytes.

def app(environ, start_response):
    corpo = b"Ola, WSGI!\n"
    start_response(
        "200 OK",
        [
            ("Content-Type", "text/plain; charset=utf-8"),
            ("Content-Length", str(len(corpo))),
        ],
    )
    return [corpo]

Retornar uma string Unicode viola a especificação. O corpo deve ser bytes. O status precisa conter o código e a frase, como 200 OK.

Executar com simple_server

from wsgiref.simple_server import make_server

with make_server("127.0.0.1", 8000, app) as servidor:
    print("http://127.0.0.1:8000")
    servidor.serve_forever()

O servidor é apropriado para testes locais e exemplos. Ele não oferece a robustez, o desempenho ou o endurecimento necessários para tráfego público.

O dicionário environ

environ contém variáveis CGI e chaves WSGI. Entre as mais comuns estão REQUEST_METHOD, PATH_INFO, QUERY_STRING, CONTENT_TYPE, CONTENT_LENGTH, SERVER_NAME, SERVER_PORT, wsgi.input, wsgi.errors e wsgi.url_scheme.

def app(environ, start_response):
    metodo = environ.get("REQUEST_METHOD", "GET")
    caminho = environ.get("PATH_INFO", "/")
    corpo = f"{metodo} {caminho}\n".encode("utf-8")
    start_response("200 OK", [
        ("Content-Type", "text/plain; charset=utf-8"),
        ("Content-Length", str(len(corpo))),
    ])
    return [corpo]

Não confie em valores derivados da requisição. Valide método, caminho, comprimento, conteúdo e headers antes de usar.

Criar environ para testes

setup_testing_defaults() preenche valores mínimos falsos para testes. Ele não deve ser usado por servidores reais.

from wsgiref.util import setup_testing_defaults

ambiente = {}
setup_testing_defaults(ambiente)
ambiente["REQUEST_METHOD"] = "POST"
ambiente["PATH_INFO"] = "/itens"

Em testes, forneça também wsgi.input como stream binário quando houver corpo.

Testar sem abrir uma porta

from io import BytesIO
from wsgiref.util import setup_testing_defaults

capturado = {}

def start_response(status, headers, exc_info=None):
    capturado["status"] = status
    capturado["headers"] = headers

ambiente = {}
setup_testing_defaults(ambiente)
ambiente["PATH_INFO"] = "/teste"
ambiente["wsgi.input"] = BytesIO(b"")

corpo = b"".join(app(ambiente, start_response))
assert capturado["status"] == "200 OK"

Esse formato testa a função diretamente e é mais rápido que iniciar o servidor em cada caso.

Validar conformidade

wsgiref.validate.validator() envolve uma aplicação e verifica diversos requisitos do protocolo.

from wsgiref.validate import validator

app_validada = validator(app)

Quando encontra uma violação, o wrapper normalmente gera AssertionError. A ausência de erros não garante conformidade completa, mas um erro relatado é um forte sinal de problema.

Headers com wsgiref.headers

A classe Headers envolve uma lista de pares e trata nomes sem diferenciar maiúsculas.

from wsgiref.headers import Headers

headers = Headers([])
headers["Content-Type"] = "text/plain; charset=utf-8"
headers.add_header(
    "Content-Disposition",
    "attachment",
    filename="relatorio.txt",
)
lista = headers.items()

Diferentemente de um dicionário, headers podem ter valores repetidos, como Set-Cookie. Use get_all() quando precisar de todos.

Não permitir headers hop-by-hop

is_hop_by_hop() identifica headers que pertencem a uma conexão específica e não devem ser enviados pela aplicação WSGI como headers normais de resposta.

from wsgiref.util import is_hop_by_hop

print(is_hop_by_hop("Connection"))

Um servidor ou middleware deve seguir a especificação e rejeitar headers incompatíveis.

Reconstruir URLs

request_uri() reconstrói a URI completa da requisição. application_uri() produz a URI-base da aplicação.

from wsgiref.util import request_uri, application_uri

url = request_uri(environ)
base = application_uri(environ)

Em aplicações atrás de proxy, o ambiente pode refletir o proxy interno. Não confie em headers encaminhados sem uma política de proxies confiáveis.

Roteamento com shift_path_info

shift_path_info() move um segmento de PATH_INFO para SCRIPT_NAME. Ele modifica o ambiente no lugar.

from wsgiref.util import shift_path_info

def roteador(environ, start_response):
    env = environ.copy()
    segmento = shift_path_info(env)
    if segmento == "api":
        return api_app(env, start_response)
    return not_found(env, start_response)

Use uma cópia quando outras camadas precisarem do caminho original.

Ler o corpo da requisição

O stream wsgi.input fornece bytes. Leia somente a quantidade permitida por CONTENT_LENGTH e imponha um limite próprio.

def ler_corpo(environ, limite=1_000_000):
    bruto = environ.get("CONTENT_LENGTH", "")
    try:
        tamanho = int(bruto) if bruto else 0
    except ValueError:
        raise ValueError("Content-Length inválido")
    if tamanho > limite:
        raise ValueError("corpo muito grande")
    return environ["wsgi.input"].read(tamanho)

Não chame read() sem limite em entradas não confiáveis.

Gerar respostas de erro

def resposta(status, texto):
    corpo = texto.encode("utf-8")
    return status, [
        ("Content-Type", "text/plain; charset=utf-8"),
        ("Content-Length", str(len(corpo))),
    ], [corpo]

Não exponha traceback e variáveis internas ao usuário. Registre detalhes em um canal protegido e devolva uma mensagem genérica.

Middleware WSGI

Middleware recebe uma aplicação e devolve outra aplicação. Ele pode adicionar logs, headers ou contexto.

def adicionar_header(app):
    def middleware(environ, start_response):
        def iniciar(status, headers, exc_info=None):
            headers.append(("X-App", "demo"))
            return start_response(status, headers, exc_info)
        return app(environ, iniciar)
    return middleware

O middleware deve preservar as regras do protocolo, fechar o iterável quando necessário e não consumir o corpo sem repassá-lo corretamente.

Fechar o iterável de resposta

Uma aplicação pode devolver um iterável com método close(). Servidores devem chamá-lo ao finalizar. Em testes manuais, use try/finally.

resultado = app(environ, start_response)
try:
    corpo = b"".join(resultado)
finally:
    fechar = getattr(resultado, "close", None)
    if fechar:
        fechar()

Servir arquivos com FileWrapper

FileWrapper transforma um objeto de arquivo binário em iterável de blocos.

from wsgiref.util import FileWrapper

arquivo = open("relatorio.pdf", "rb")
return FileWrapper(arquivo, blksize=64 * 1024)

Valide o caminho antes de abrir e defina headers corretos. Em produção, servidores e proxies costumam oferecer mecanismos mais eficientes.

Tipos estáticos

Desde Python 3.11, wsgiref.types fornece protocolos e aliases como WSGIApplication, WSGIEnvironment e StartResponse.

from wsgiref.types import WSGIApplication

aplicacao: WSGIApplication = app

Tipos ajudam a detectar retornos Unicode, headers incorretos e assinaturas incompatíveis antes da execução.

WSGI é síncrono

WSGI modela uma chamada síncrona por requisição. Ele continua relevante para frameworks tradicionais, mas não representa WebSockets ou concorrência assíncrona nativa como ASGI.

Não tente retornar uma coroutine de uma aplicação WSGI. Use um framework e servidor compatíveis com a interface necessária.

Por que não usar simple_server em produção

O servidor de referência não é uma plataforma endurecida. Ele não deve ficar diretamente exposto à internet. Faltam recursos esperados de produção, como estratégias robustas de concorrência, proteção contra tráfego lento, gestão completa de limites, TLS operacional, reinicialização de workers e observabilidade.

Exemplo completo

from wsgiref.simple_server import make_server
from wsgiref.validate import validator


def app(environ, start_response):
    if environ.get("PATH_INFO") != "/":
        corpo = b"Not Found\n"
        start_response("404 Not Found", [
            ("Content-Type", "text/plain"),
            ("Content-Length", str(len(corpo))),
        ])
        return [corpo]

    corpo = b"Hello from WSGI\n"
    start_response("200 OK", [
        ("Content-Type", "text/plain; charset=utf-8"),
        ("Content-Length", str(len(corpo))),
    ])
    return [corpo]

with make_server("127.0.0.1", 8000, validator(app)) as httpd:
    httpd.serve_forever()

Erros frequentes

  • Retornar string em vez de bytes.
  • Usar o servidor simples em produção.
  • Ler o corpo sem limite.
  • Confiar em headers de proxy arbitrários.
  • Expor traceback em respostas.
  • Modificar environ sem considerar middleware.
  • Ignorar o fechamento do iterável.

Boas práticas

  • Use validator() nos testes.
  • Teste a aplicação diretamente sem rede.
  • Defina Content-Type e Content-Length.
  • Trabalhe com bytes no corpo.
  • Imponha limites de entrada.
  • Use servidor WSGI adequado em produção.
  • Escolha ASGI quando precisar de async nativo.

Conteúdos relacionados

Veja selectors, contextvars, platform, sysconfig e pydoc.

Consulte a documentação oficial do wsgiref e a PEP 3333.

Conclusão

wsgiref é excelente para aprender WSGI, testar aplicações e validar conformidade. Ele mostra claramente o contrato entre servidor e aplicação, mas seu servidor simples não deve receber tráfego de produção. Use-o como referência e ferramenta de desenvolvimento.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Protocolo seguro na internet representando preparação Unicode com stringprep no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    stringprep no Python: prepare Unicode

    Aprenda stringprep no Python para aplicar tabelas do RFC 3454, mapear Unicode, bloquear caracteres proibidos e validar regras bidirecionais.

    Ler mais

    Tempo de leitura: 7 minutos
    12/08/2026
    Rede de conexões representando I/O não bloqueante com selectors no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    selectors no Python: I/O não bloqueante

    Aprenda selectors no Python para monitorar vários sockets, eventos de leitura e escrita, timeouts e conexões não bloqueantes com segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    11/08/2026
    Fluxo de dados em rede representando contexto assíncrono com contextvars no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    contextvars no Python: contexto assíncrono

    Aprenda contextvars no Python para armazenar estado por tarefa, evitar vazamentos em asyncio, copiar contextos e restaurar valores com tokens.

    Ler mais

    Tempo de leitura: 6 minutos
    11/08/2026
    Código de programação representando operações como funções com operator no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    operator no Python: operações como funções

    Aprenda operator no Python para usar operações como funções, ordenar campos, acessar itens, chamar métodos e trabalhar com pipelines funcionais.

    Ler mais

    Tempo de leitura: 5 minutos
    11/08/2026
    Alfabeto tridimensional representando normalização Unicode com unicodedata no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    unicodedata no Python: normalize Unicode

    Aprenda unicodedata no Python para normalizar Unicode, consultar nomes, categorias, números, caracteres combinantes e largura de exibição.

    Ler mais

    Tempo de leitura: 6 minutos
    11/08/2026
    Rede de servidores representando gerenciamento de recursos com ExitStack no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ExitStack no Python: gerencie recursos

    Aprenda ExitStack no Python para gerenciar arquivos, conexões e limpezas dinâmicas com segurança e ordem previsível.

    Ler mais

    Tempo de leitura: 6 minutos
    11/08/2026