Flask

Flask

Tutorial Flask: Cabeceras HTTP

Aprende a manejar cabeceras HTTP en Flask para crear APIs seguras y flexibles con ejemplos prácticos de lectura y envío de headers.

Aprende Flask y certifícate

Lectura de headers con request.headers

Las cabeceras HTTP contienen información adicional sobre la petición que realiza el cliente, como el tipo de contenido que acepta, el navegador utilizado, tokens de autenticación, o datos personalizados que nuestra aplicación necesite procesar. Flask proporciona acceso directo a estas cabeceras a través del objeto request.headers.

El objeto request.headers se comporta como un diccionario que contiene todas las cabeceras enviadas por el cliente. Podemos acceder a cabeceras específicas utilizando diferentes métodos según nuestras necesidades.

Acceso básico a cabeceras

La forma más directa de leer una cabecera es utilizando la sintaxis de diccionario:

from flask import Flask, request

app = Flask(__name__)

@app.route('/info')
def mostrar_info():
    user_agent = request.headers['User-Agent']
    return f"Tu navegador es: {user_agent}"

Sin embargo, este enfoque puede generar un error KeyError si la cabecera no existe. Para evitar esto, utilizamos el método get():

@app.route('/info-seguro')
def mostrar_info_seguro():
    user_agent = request.headers.get('User-Agent', 'Navegador desconocido')
    content_type = request.headers.get('Content-Type')
    
    if content_type:
        return f"Navegador: {user_agent}, Tipo de contenido: {content_type}"
    else:
        return f"Navegador: {user_agent}, Sin tipo de contenido especificado"

Cabeceras comunes en aplicaciones web

Algunas cabeceras estándar que frecuentemente necesitamos leer en nuestras APIs incluyen:

@app.route('/analizar-peticion')
def analizar_peticion():
    # Información del cliente
    user_agent = request.headers.get('User-Agent', 'Desconocido')
    
    # Tipo de contenido aceptado
    accept = request.headers.get('Accept', '*/*')
    
    # Idioma preferido
    accept_language = request.headers.get('Accept-Language', 'es')
    
    # Información de autorización (si existe)
    authorization = request.headers.get('Authorization')
    
    info = {
        'navegador': user_agent,
        'acepta': accept,
        'idioma': accept_language,
        'tiene_auth': authorization is not None
    }
    
    return info

Trabajando con cabeceras personalizadas

Las aplicaciones modernas frecuentemente utilizan cabeceras personalizadas para transmitir información específica. Por convención, estas cabeceras suelen comenzar con X-:

@app.route('/api/datos')
def obtener_datos():
    # Leer cabecera personalizada para identificar la aplicación cliente
    app_version = request.headers.get('X-App-Version')
    api_key = request.headers.get('X-API-Key')
    
    if not api_key:
        return {'error': 'API Key requerida'}, 401
    
    # Procesar según la versión de la aplicación
    if app_version and app_version.startswith('2.'):
        return {'datos': 'Formato v2', 'version': app_version}
    else:
        return {'datos': 'Formato v1', 'version': app_version or 'legacy'}

Iteración sobre todas las cabeceras

Cuando necesitamos examinar todas las cabeceras recibidas, podemos iterar sobre el objeto request.headers:

@app.route('/debug/headers')
def mostrar_todas_cabeceras():
    cabeceras = {}
    
    for nombre, valor in request.headers:
        cabeceras[nombre] = valor
    
    return {
        'total_cabeceras': len(cabeceras),
        'cabeceras': cabeceras
    }

También podemos filtrar cabeceras específicas durante la iteración:

@app.route('/debug/cabeceras-personalizadas')
def mostrar_cabeceras_personalizadas():
    cabeceras_x = {}
    
    for nombre, valor in request.headers:
        if nombre.startswith('X-'):
            cabeceras_x[nombre] = valor
    
    return {
        'cabeceras_personalizadas': cabeceras_x,
        'cantidad': len(cabeceras_x)
    }

Validación y procesamiento de cabeceras

En aplicaciones reales, es común validar el formato de las cabeceras antes de procesarlas:

@app.route('/api/upload')
def procesar_upload():
    content_type = request.headers.get('Content-Type', '')
    content_length = request.headers.get('Content-Length')
    
    # Validar tipo de contenido
    if not content_type.startswith('multipart/form-data'):
        return {'error': 'Tipo de contenido no válido'}, 400
    
    # Validar tamaño del contenido
    if content_length:
        try:
            size = int(content_length)
            if size > 10 * 1024 * 1024:  # 10MB máximo
                return {'error': 'Archivo demasiado grande'}, 413
        except ValueError:
            return {'error': 'Content-Length inválido'}, 400
    
    return {'status': 'Listo para procesar', 'size': content_length}

El manejo adecuado de cabeceras HTTP nos permite crear APIs más robustas y flexibles, capaces de adaptarse a diferentes tipos de clientes y escenarios de uso. La información contenida en las cabeceras es fundamental para implementar funcionalidades como autenticación, negociación de contenido, y personalización de respuestas.

Envío de headers personalizados

Además de leer las cabeceras que envía el cliente, Flask nos permite enviar cabeceras personalizadas en nuestras respuestas HTTP. Esto resulta esencial para comunicar información adicional al cliente, implementar políticas de seguridad, o proporcionar metadatos sobre la respuesta.

Flask ofrece varias formas de añadir cabeceras a nuestras respuestas, desde métodos simples hasta enfoques más avanzados que nos dan control total sobre la estructura de la respuesta HTTP.

Envío básico con make_response

La forma más directa de enviar cabeceras personalizadas es utilizando la función make_response() de Flask:

from flask import Flask, make_response

app = Flask(__name__)

@app.route('/api/datos')
def enviar_con_cabeceras():
    datos = {'mensaje': 'Datos procesados correctamente'}
    
    # Crear respuesta personalizada
    response = make_response(datos)
    
    # Añadir cabeceras personalizadas
    response.headers['X-API-Version'] = '1.2.0'
    response.headers['X-Rate-Limit'] = '100'
    response.headers['X-Processing-Time'] = '0.045s'
    
    return response

Cabeceras de seguridad y políticas

Las cabeceras de seguridad son fundamentales en aplicaciones web modernas. Podemos implementarlas fácilmente en nuestros endpoints:

@app.route('/api/secure-data')
def datos_seguros():
    datos = {'informacion': 'Datos confidenciales'}
    
    response = make_response(datos)
    
    # Cabeceras de seguridad
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['X-XSS-Protection'] = '1; mode=block'
    response.headers['Strict-Transport-Security'] = 'max-age=31536000'
    
    return response

Control de caché con cabeceras

El control de caché mediante cabeceras HTTP permite optimizar el rendimiento de nuestra API:

@app.route('/api/contenido-estatico')
def contenido_con_cache():
    contenido = {'datos': 'Información que cambia poco'}
    
    response = make_response(contenido)
    
    # Configurar caché por 1 hora
    response.headers['Cache-Control'] = 'public, max-age=3600'
    response.headers['ETag'] = '"v1.0-static-content"'
    
    return response

@app.route('/api/contenido-dinamico')
def contenido_sin_cache():
    contenido = {'timestamp': '2024-01-15T10:30:00Z', 'datos': 'Información en tiempo real'}
    
    response = make_response(contenido)
    
    # Evitar caché para contenido dinámico
    response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
    response.headers['Pragma'] = 'no-cache'
    response.headers['Expires'] = '0'
    
    return response

Cabeceras CORS para APIs públicas

Cuando desarrollamos APIs que serán consumidas desde navegadores web, necesitamos configurar cabeceras CORS (Cross-Origin Resource Sharing):

@app.route('/api/publico')
def endpoint_publico():
    datos = {'mensaje': 'API pública accesible desde cualquier origen'}
    
    response = make_response(datos)
    
    # Configurar CORS
    response.headers['Access-Control-Allow-Origin'] = '*'
    response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
    response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
    
    return response

@app.route('/api/restringido')
def endpoint_restringido():
    datos = {'mensaje': 'API con acceso limitado'}
    
    response = make_response(datos)
    
    # CORS restrictivo para dominios específicos
    response.headers['Access-Control-Allow-Origin'] = 'https://miapp.com'
    response.headers['Access-Control-Allow-Credentials'] = 'true'
    
    return response

Cabeceras informativas y de metadatos

Las cabeceras informativas proporcionan contexto adicional sobre la respuesta o el estado del servidor:

@app.route('/api/estadisticas')
def obtener_estadisticas():
    stats = {
        'usuarios_activos': 1250,
        'peticiones_hoy': 45000
    }
    
    response = make_response(stats)
    
    # Información sobre la respuesta
    response.headers['X-Total-Records'] = '2'
    response.headers['X-Server-Region'] = 'EU-West'
    response.headers['X-Response-Source'] = 'cache'
    response.headers['X-Request-ID'] = 'req-12345-abcde'
    
    return response

Método alternativo con tuplas

Flask también permite enviar cabeceras utilizando tuplas en el return, lo que resulta más conciso para casos simples:

@app.route('/api/simple')
def respuesta_simple():
    datos = {'status': 'ok'}
    cabeceras = {
        'X-API-Version': '2.0',
        'X-Custom-Header': 'valor-personalizado'
    }
    
    return datos, 200, cabeceras

@app.route('/api/error-personalizado')
def error_con_cabeceras():
    error = {'error': 'Recurso no encontrado'}
    cabeceras = {
        'X-Error-Code': 'RESOURCE_NOT_FOUND',
        'X-Retry-After': '300'
    }
    
    return error, 404, cabeceras

Cabeceras dinámicas basadas en condiciones

En muchos casos, las cabeceras que enviamos dependen de la lógica de negocio o del estado de la aplicación:

@app.route('/api/usuario/<int:user_id>')
def obtener_usuario(user_id):
    # Simular obtención de usuario
    usuario = {'id': user_id, 'nombre': 'Juan Pérez'}
    
    response = make_response(usuario)
    
    # Cabeceras condicionales
    if user_id < 1000:
        response.headers['X-User-Type'] = 'legacy'
        response.headers['X-Migration-Required'] = 'true'
    else:
        response.headers['X-User-Type'] = 'modern'
    
    # Cabecera con información calculada
    response.headers['X-User-Hash'] = f"user-{user_id}-hash"
    
    return response

Middleware para cabeceras globales

Para aplicar cabeceras comunes a todas las respuestas, podemos utilizar decoradores o middleware:

@app.after_request
def agregar_cabeceras_globales(response):
    # Cabeceras que se añaden a todas las respuestas
    response.headers['X-Powered-By'] = 'Flask-API'
    response.headers['X-Content-Type-Options'] = 'nosniff'
    
    return response

@app.route('/api/cualquier-endpoint')
def endpoint_ejemplo():
    # Este endpoint automáticamente tendrá las cabeceras globales
    return {'mensaje': 'Las cabeceras globales se añaden automáticamente'}

El envío de cabeceras personalizadas nos permite crear APIs más informativas, seguras y eficientes. Estas cabeceras facilitan la comunicación entre cliente y servidor, proporcionando metadatos valiosos que mejoran la experiencia del desarrollador y la funcionalidad de la aplicación.

Aprende Flask online

Otras lecciones de Flask

Accede a todas las lecciones de Flask y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Flask y certifícate

Ejercicios de programación de Flask

Evalúa tus conocimientos de esta lección Cabeceras HTTP con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.