Flask Solicitudes y Respuestas

El núcleo de una aplicación web es «recibir solicitudes y devolver respuestas». Este capítulo explica en detalle cómo leer los datos de solicitud y construir varios tipos de respuestas en Flask.

Todos los ejemplos se pueden ejecutar y verificar directamente en el servidor de desarrollo.


Objeto de solicitud — request

Flask, a través de un objeto proxy global,requestproporciona toda la información de la solicitud.

Aunque es un objeto «global», Flask internamente utiliza un mecanismo de aislamiento de hilos para garantizar que cada solicitud obtenga sus propios datos.

Ejemplo

# Importar request, un objeto proxy thread-safe
from flask import Flask, request

app = Flask(__name__)

@app.route("/debug")
def debug():
    # Mostrar varias informaciones de la solicitud para ayudar a entender el objeto request
    return f"""
<h1>Información de depuración de la solicitud</h1>
    <ul>
<li>Método: {request.method}</li>
<li>Ruta: {request.path}</li>
<li>URL completa: {request.url}</li>
<li>Host: {request.host}</li>
<li>IP del cliente: {request.remote_addr}</li>
        <li>User-Agent:{request.headers.get('User-Agent')}</li>
    </ul>
    """

Accedehttp://127.0.0.1:5000/debug?foo=bary verás la información detallada de la solicitud.


Obtener parámetros de consulta — request.args

En la URL,?los parámetros posteriores se denominan parámetros de consulta (Query String).

Usarequest.argspara leerlos; se comporta como un diccionario:

Ejemplo

@app.route("/search")
def search():
    # request.args.get() obtiene parámetros de forma segura; devuelve None si la clave no existe
    keyword = request.args.get("keyword", "")  # Obtener la palabra clave de búsqueda
    page = request.args.get("page", "1")       # Obtener el número de página, por defecto 1
    return f"""
<h1>Resultados de búsqueda</h1>
<p>Palabra clave: {keyword}</p>
<p>Página: {page}</p>
    """

# Prueba: /search?keyword=flask&page=2
# Prueba: /search?keyword=EJEMPLO

Se recomienda utilizar.get()en lugar de acceder directamente por índice,request.args["key"]porque esto último lanza una excepción cuando la clave no existe,KeyErrorlo que provoca que se devuelva una página de error 400.


Obtener datos de formulario — request.form

Para procesar los datos de formulario enviados mediante una solicitud POST, usarequest.form:

Ejemplo

@app.route("/register", methods=["GET", "POST"])
def register():
    if request.method == "POST":
        # Usar el valor del campo name="username" del formulario
        username = request.form.get("username", "")
        password = request.form.get("password", "")

        if not username or not password:
            return "<h1>Error</h1><p>El nombre de usuario y la contraseña no pueden estar vacíos</p>", 400

        return f"<h1>Registro exitoso</h1><p>¡Bienvenido {username} a EJEMPLO!</p>"

    # Mostrar el formulario de registro cuando la solicitud es GET
    return """
<h1>Registro</h1>
    <form method="post">
<p>Usuario: <input type="text" name="username"></p>
<p>Contraseña: <input type="password" name="password"></p>
<p><input type="submit" value="Registrarse"></p>
    </form>
    """

Nota: en la página HTML que procesa el formulario,<form>debe configurarsemethod="post", de lo contrario los datos se envían a través de la cadena de consulta de la URL (método GET) y la información sensible queda expuesta en la URL.


Obtener datos JSON — request.json

En el desarrollo web moderno, JSON es el formato de datos más común.

Cuando el cliente envíaContent-Type: application/jsonuna solicitud de este tipo, utilizarequest.jsonpara obtener los datos parseados:

Ejemplo

@app.post("/api/user")
def create_user():
    # request.json devuelve los datos JSON ya convertidos en un dict de Python
    data = request.json  # Por ejemplo, el cliente envía {"name": "ejemplo", "email": "[email protected]"}

    # Obtener los campos de forma segura
    name = data.get("name", "Desconocido")
    email = data.get("email", "No proporcionado")

    # Devolver el dict directamente; Flask lo convierte automáticamente en una respuesta JSON
    return {
        "message": "Usuario creado correctamente",
        "name": name,
        "email": email
    }

Puedes usarcurlo Postman para probar:

$ curl -X POST http://127.0.0.1:5000/api/user \
  -H "Content-Type: application/json" \
  -d '{"name": "ejemplo", "email": "[email protected]"}'
{
  "message": "用户创建成功",
  "name": "ejemplo",
  "email": "[email protected]"
}

Subida de archivos

Para procesar los archivos subidos, usarequest.files, junto con el proporcionado por Werkzeugsecure_filename()para garantizar que el nombre de archivo sea seguro:

Ejemplo

import os
from werkzeug.utils import secure_filename  # Filtrar caracteres peligrosos del nombre de archivo

# Configurar el directorio de subida (en un proyecto real debería leerse de la configuración)
UPLOAD_DIR = "uploads"

@app.route("/upload", methods=["GET", "POST"])
def upload_file():
    if request.method == "POST":
        # Comprobar si se ha subido un archivo
        if "file" not in request.files:
            return "No se seleccionó ningún archivo", 400

        file = request.files["file"]

        # El usuario puede haber enviado un formulario vacío (sin seleccionar archivo)
        if file.filename == "":
            return "El nombre de archivo está vacío", 400

        # secure_filename filtra caracteres peligrosos como path traversal
        # Por ejemplo, "../../etc/passwd" se convierte en "etc_passwd"
        filename = secure_filename(file.filename)

        # Asegurar que el directorio de subida exista
        os.makedirs(UPLOAD_DIR, exist_ok=True)

        # Guardar el archivo
        file.save(os.path.join(UPLOAD_DIR, filename))
        return f"Archivo {filename} subido correctamente"

    # Mostrar el formulario de subida cuando la solicitud es GET
    return """
<h1>Subir archivo</h1>
    <form method="post" enctype="multipart/form-data">
        <p><input type="file" name="file"></p>
<p><input type="submit" value="Subir"></p>
    </form>
    """

Si el formulario tiene un campo de subida de archivos,<form>la etiqueta debe configurarseenctype="multipart/form-data", de lo contrario el navegador no enviará los datos del archivo.


Guía completa de tipos de respuesta

Las funciones de vista pueden devolver varios tipos de datos; Flask los convierte automáticamente en respuestas HTTP adecuadas.

Entender esta regla de conversión es clave para dominar Flask.

Devolver una cadena

La cadena devuelta se usa como cuerpo de la respuesta HTML; el código de estado por defecto es 200 y el Content-Type estext/html。

Devolver diccionario o lista (JSON)

Se llama automáticamente ajsonify()para convertir a una respuesta JSON; el Content-Type esapplication/json。

Ejemplo

@app.get("/api/posts")
def get_posts():
    # Devolver un dict; Flask lo convierte automáticamente a JSON
    return {
        "status": "ok",
        "data": [
            {"id": 1, "title": "Tutorial de introducción a Flask"},
            {"id": 2, "title": "Diseño de API RESTful"},
        ]
    }

@app.get("/api/tags")
def get_tags():
    # Devolver una lista; Flask la convierte automáticamente a JSON
    return ["Python", "Flask", "Desarrollo web", "EJEMPLO"]

Devolver tuplas — controlar el código de estado y las cabeceras de respuesta

El formato de tupla es muy práctico y tiene tres formas:

Ejemplo

# Forma 1: (body, status_code)
@app.get("/not-found")
def not_found():
    return "<h1>Página no encontrada</h1>", 404

# Forma 2: (body, headers_dict)
@app.get("/custom-header")
def custom_header():
    return "OK", {"X-Custom-Header": "ejemplo"}

# Forma 3: (body, status_code, headers_dict)
@app.post("/api/login")
def api_login():
    # Devolver token y código de estado personalizado
    return {"token": "abc123", "user": "ejemplo"}, 201, {"X-RateLimit": "100"}

Redirección

Usaredirect()la función para redirigir al usuario a otra URL:

Ejemplo

from flask import Flask, redirect, url_for

app = Flask(__name__)

@app.route("/")
def index():
    # Redirigir a la página de inicio de sesión al acceder a la página principal
    return redirect(url_for("login"))

@app.route("/login")
def login():
    return "<h1>Por favor, inicie sesión primero</h1>"

@app.route("/old-page")
def old_page():
    # La página antigua se migra permanentemente a la nueva, usando el código de estado 301
    return redirect(url_for("new_page"), code=301)

@app.route("/new-page")
def new_page():
    return "<h1>Esta es la nueva página</h1>"

El código de estado de redirección por defecto es303 See Other,aplicable a la redirección tras el envío de formularios (patrón PRG).

Para escenarios de migración permanente, se debe usarcode=301。


Terminar la solicitud — abort

Usarabort()Terminar inmediatamente la solicitud actual y devolver un error HTTP:

Ejemplo

from flask import abort

@app.route("/post/<int:post_id>")
def view_post(post_id):
    # Supongamos que el ID del artículo es solo 1-100
    if post_id < 1 or post_id > 100:
        # Devuelve 404 inmediatamente, el código posterior no se ejecutará
        abort(404)

    return f<h1>Artículo #{post_id}</h1>

@app.route("/admin")
def admin():
    # Devuelve 403 cuando el acceso no está autorizado
    abort(403, description=No tienes permiso para acceder a esta página)

make_response — controlar la respuesta manualmente

Cuando necesite configurar manualmente cookies o encabezados de respuesta personalizados, utilicemake_response():

Ejemplo

from flask import make_response

@app.route("/set-cookie")
def set_cookie():
    # make_response envuelve el valor de retorno de la función de vista en un objeto Response
    resp = make_response(<h1>Cookie establecida</h1>)

    # Establecer Cookie en el objeto Response
    resp.set_cookie("theme", "dark", max_age=60*60*24)  # Válido por 24 horas
    resp.set_cookie("lang", "zh-CN")

    # También se pueden personalizar los encabezados de respuesta
    resp.headers["X-Powered-By"] = "EJEMPLO-Flask"

    return resp

Tabla de referencia rápida de atributos comunes de request

Atributo Tipo Descripción Valor de ejemplo
request.method str Método de solicitud HTTP "GET", "POST"
request.path str Ruta de URL (sin dominio) "/search"
request.args MultiDict Parámetros de consulta de URL ?keyword=flask&page=1
request.form MultiDict Datos de formulario POST username=admin
request.json dict o None Cuerpo de solicitud JSON (parseado) {"name": "ejemplo"}
request.files FileMultiDict Archivos subidos request.files["avatar"]
request.headers Headers Encabezados de solicitud request.headers["User-Agent"]
request.cookies dict Cookie enviada por el cliente request.cookies.get("theme")
request.remote_addr str Dirección IP del cliente "127.0.0.1"
Otras extensiones