Flask
Tutorial Flask: Manejo de errores y códigos de estado HTTP
Dominio de gestión de errores en Flask para APIs RESTful. Aprende de personalización de excepciones, respuestas JSON y uso de códigos de estado HTTP.
Aprende Flask GRATIS y certifícateGestión de errores en endpoints REST
En el desarrollo de endpoints REST con Flask, es crucial manejar adecuadamente los errores para proporcionar respuestas coherentes y útiles a los clientes. Una buena gestión de errores mejora la experiencia del usuario y facilita el mantenimiento de la aplicación.
Para gestionar excepciones en Flask, se puede utilizar el decorador @app.errorhandler
para capturar errores específicos y devolver una respuesta personalizada. Por ejemplo, para manejar el error 404 Not Found
:
@app.errorhandler(404)
def recurso_no_encontrado(error):
return jsonify({'error': 'Recurso no encontrado'}), 404
Este código captura las excepciones 404
y retorna una respuesta en formato JSON con un mensaje claro. Es importante mantener la consistencia en el formato de las respuestas, utilizando siempre JSON en APIs RESTful.
Además de los errores estándar, es posible definir excepciones personalizadas para manejar situaciones específicas de la aplicación. Por ejemplo:
class ErrorDeNegocio(Exception):
pass
@app.errorhandler(ErrorDeNegocio)
def manejar_error_de_negocio(error):
respuesta = jsonify({'error': str(error)})
respuesta.status_code = 400
return respuesta
Con esta excepción personalizada ErrorDeNegocio
, se pueden lanzar errores específicos en funciones donde sea necesario:
@app.route('/procesar_datos', methods=['POST'])
def procesar_datos():
datos = request.get_json()
if not datos or 'campo_requerido' not in datos:
raise ErrorDeNegocio('Falta el campo requerido')
# Procesamiento de datos...
return jsonify({'mensaje': 'Datos procesados correctamente'}), 200
Es recomendable utilizar bloques try-except dentro de los endpoints para capturar excepciones inesperadas y evitar que la aplicación se detenga abruptamente:
@app.route('/calcular', methods=['GET'])
def calcular():
try:
resultado = realizar_calculo()
return jsonify({'resultado': resultado}), 200
except ZeroDivisionError:
return jsonify({'error': 'División por cero no permitida'}), 400
except Exception as e:
app.logger.error(f'Error inesperado: {e}')
return jsonify({'error': 'Error interno del servidor'}), 500
En este ejemplo, se manejan específicamente las excepciones ZeroDivisionError
y se captura cualquier otra excepción general, registrándola en el log de la aplicación.
Es esencial registrar los errores para facilitar la depuración y monitoreo de la aplicación en producción. Flask permite configurar el registro de manera flexible:
import logging
from logging.handlers import RotatingFileHandler
if not app.debug:
gestor_registro = RotatingFileHandler('errores.log', maxBytes=10000, backupCount=3)
gestor_registro.setLevel(logging.ERROR)
app.logger.addHandler(gestor_registro)
Con esta configuración, los errores se almacenan en el archivo errores.log
, permitiendo monitorear y analizar incidentes.
Para centralizar la gestión de errores, se puede crear una función genérica:
def manejar_errores(error, mensaje, codigo_estado):
respuesta = jsonify({'error': mensaje})
respuesta.status_code = codigo_estado
return respuesta
Y utilizarla en los manejadores de errores:
@app.errorhandler(Unauthorized)
def manejar_no_autorizado(error):
return manejar_errores(error, 'No autorizado', 401)
@app.errorhandler(BadRequest)
def manejar_peticion_incorrecta(error):
return manejar_errores(error, 'Petición incorrecta', 400)
Esto permite simplificar el código y asegurar que todas las respuestas de error tengan un formato uniforme.
Al implementar una gestión de errores robusta, se garantiza que la aplicación RESTful sea más confiable y ofrezca información útil a los clientes cuando ocurran problemas, facilitando además el trabajo de los desarrolladores en la identificación y solución de errores.
Respuestas de error estándar en JSON
En el desarrollo de APIs REST con Flask, es fundamental proporcionar respuestas de error estándar en formato JSON para garantizar una comunicación clara y consistente con los clientes. Estandarizar las respuestas de error facilita la interpretación de los fallos y mejora la experiencia del desarrollador al consumir la API.
Una práctica recomendada es definir una estructura de error común que todas las respuestas de error seguirán. Esto puede incluir campos como un código de error interno, un mensaje descriptivo y, opcionalmente, detalles adicionales. Por ejemplo:
{
"error": {
"codigo": "RECURSO_NO_ENCONTRADO",
"mensaje": "El recurso solicitado no existe.",
"detalles": null
}
}
Este formato permite que los clientes de la API programen sus aplicaciones para manejar errores de manera consistente, basándose en los códigos de error definidos.
Para implementar este estándar en Flask, se puede crear una función auxiliar que genere estas respuestas. Por ejemplo:
def respuesta_error(codigo_error, mensaje, detalles=None, estado_http=400):
respuesta = jsonify({
'error': {
'codigo': codigo_error,
'mensaje': mensaje,
'detalles': detalles
}
})
respuesta.status_code = estado_http
return respuesta
Con esta función respuesta_error
, es sencillo retornar errores en cualquier parte de los endpoints:
@app.route('/usuario/<int:id_usuario>', methods=['GET'])
def obtener_usuario(id_usuario):
usuario = Usuario.query.get(id_usuario)
if not usuario:
return respuesta_error(
codigo_error='USUARIO_NO_ENCONTRADO',
mensaje='El usuario solicitado no existe.',
estado_http=404
)
return jsonify(usuario_schema.dump(usuario)), 200
Es importante utilizar códigos de error significativos que permitan identificar rápidamente el tipo de fallo. Estos códigos pueden ser cadenas de texto en mayúsculas con guiones bajos, representando de forma clara el error. Mantener una documentación actualizada de todos los códigos de error ayuda a los consumidores de la API a entender y manejar los errores adecuadamente.
Además, es posible extender la información de los errores añadiendo campos personalizados en el objeto de error. Por ejemplo, si se produce un error de validación, se pueden incluir los campos que fallaron:
def respuesta_error_validacion(errores):
return respuesta_error(
codigo_error='ERROR_VALIDACION',
mensaje='Los datos proporcionados no son válidos.',
detalles=errores,
estado_http=422
)
Y utilizarla en un endpoint:
@app.route('/usuarios', methods=['POST'])
def crear_usuario():
datos = request.get_json()
errores = schema_usuario.validate(datos)
if errores:
return respuesta_error_validacion(errores)
usuario = Usuario(**datos)
db.session.add(usuario)
db.session.commit()
return jsonify(usuario_schema.dump(usuario)), 201
En este ejemplo, se emplea Marshmallow para validar los datos de entrada y, en caso de errores, se devuelve una respuesta de error estándar con los detalles de validación.
Para capturar y manejar excepciones globalmente, Flask permite registrar un manejador de errores genérico:
@app.errorhandler(Exception)
def manejar_excepcion_global(error):
app.logger.error(f'Error no controlado: {error}')
return respuesta_error(
codigo_error='ERROR_INTERNO',
mensaje='Ha ocurrido un error interno.',
estado_http=500
)
Este manejador captura todas las excepciones no controladas y devuelve una respuesta de error estándar, evitando exponer información sensible al cliente.
Es recomendable definir excepciones personalizadas que representen errores específicos de la aplicación. Por ejemplo:
class ErrorAplicacion(Exception):
def __init__(self, codigo_error, mensaje, estado_http=400):
self.codigo_error = codigo_error
self.mensaje = mensaje
self.estado_http = estado_http
super().__init__(mensaje)
@app.errorhandler(ErrorAplicacion)
def manejar_error_aplicacion(error):
return respuesta_error(
codigo_error=error.codigo_error,
mensaje=error.mensaje,
estado_http=error.estado_http
)
Con esta estructura, se pueden lanzar excepciones en cualquier parte del código y asegurarse de que se devuelven como respuestas de error coherentes:
@app.route('/operacion')
def realizar_operacion():
try:
# Lógica de negocio
if algun_problema:
raise ErrorAplicacion('OPERACION_NO_PERMITIDA', 'La operación solicitada no está permitida.', 403)
return jsonify({'resultado': 'Operación exitosa'}), 200
except ErrorAplicacion as e:
raise e # Será capturada por el manejador
except Exception as e:
app.logger.error(f'Error inesperado: {e}')
raise ErrorAplicacion('ERROR_INTERNO', 'Se ha producido un error inesperado.', 500)
Al estandarizar las respuestas de error, se mejora la consistencia y se facilita el trabajo de los desarrolladores que consumen la API. Además, se promueve una comunicación clara de los errores, lo que es esencial en aplicaciones modernas.
También es importante configurar el entorno de desarrollo para que, en caso de error, no se muestren trazas de pila al cliente, ya que podrían exponer información sensible. En su lugar, estas trazas deben ser registradas en los logs de la aplicación para su posterior análisis.
Para integrar esta estrategia de respuestas de error estándar en toda la aplicación, se puede crear un módulo dedicado a la gestión de errores, manteniendo el código organizado y modularizado:
# en errors.py
from flask import Blueprint
errors = Blueprint('errors', __name__)
@errors.app_errorhandler(404)
def error_404(error):
return respuesta_error('RECURSO_NO_ENCONTRADO', 'El recurso solicitado no existe.', 404)
@errors.app_errorhandler(500)
def error_500(error):
return respuesta_error('ERROR_INTERNO', 'Error interno del servidor.', 500)
Y registrarlo en la aplicación principal:
# en app.py
from errors import errors
app.register_blueprint(errors)
De esta forma, los blueprints de Flask permiten centralizar la gestión de errores y mantener el código limpio y mantenible.
Uso adecuado de códigos de estado
El uso correcto de los códigos de estado HTTP es esencial en la construcción de APIs RESTful con Flask. Estos códigos proporcionan información al cliente sobre el resultado de una solicitud, facilitando la interpretación de las respuestas y la gestión de errores. Es importante seleccionar el código de estado adecuado para cada situación, garantizando una comunicación clara entre el servidor y el cliente.
En Flask, al devolver una respuesta desde un endpoint, se puede especificar el código de estado HTTP utilizando el parámetro status_code
o devolviendo una tupla con el contenido y el código. Por ejemplo:
from flask import jsonify
@app.route('/recurso', methods=['GET'])
def obtener_recurso():
datos = obtener_datos()
return jsonify(datos), 200 # Código 200 OK
En este ejemplo, se devuelve un código 200 OK, indicando que la solicitud ha sido exitosa y el recurso se ha obtenido correctamente. Es una buena práctica utilizar el código de estado que más se ajuste a la operación realizada.
Códigos de estado para operaciones exitosas
Para operaciones que se completan correctamente, se deben utilizar los códigos de estado en la familia de los 2xx. Algunos de los más comunes son:
200 OK: Indica que la solicitud se ha procesado correctamente. Se usa en operaciones GET, PUT o DELETE cuando se devuelve el resultado directamente.
201 Created: Se emplea cuando se ha creado un nuevo recurso como resultado de una operación POST. Es recomendable incluir en la respuesta la ubicación del nuevo recurso mediante la cabecera Location
o en el cuerpo de la respuesta.
@app.route('/usuarios', methods=['POST'])
def crear_usuario():
datos_usuario = request.get_json()
nuevo_usuario = Usuario(**datos_usuario)
db.session.add(nuevo_usuario)
db.session.commit()
return jsonify({'id': nuevo_usuario.id}), 201 # Código 201 Created
- 204 No Content: Se utiliza cuando la solicitud se ha procesado con éxito pero no se devuelve contenido en el cuerpo de la respuesta. Es común en operaciones DELETE.
@app.route('/usuarios/<int:id_usuario>', methods=['DELETE'])
def eliminar_usuario(id_usuario):
usuario = Usuario.query.get_or_404(id_usuario)
db.session.delete(usuario)
db.session.commit()
return '', 204 # Código 204 No Content
Códigos de estado para redirecciones
Los códigos de estado de la serie 3xx indican redirecciones. En APIs RESTful, su uso es menos frecuente, pero pueden ser útiles en ciertos casos.
- 301 Moved Permanently: Indica que el recurso se ha movido de forma permanente a una nueva URL.
- 302 Found: Señala que el recurso se ha encontrado en otra ubicación temporalmente.
- 304 Not Modified: Se utiliza en mecanismos de caché para indicar que el recurso no ha cambiado y el cliente puede utilizar la versión en caché.
Códigos de estado para errores del cliente
Los códigos de estado de la serie 4xx indican errores que han ocurrido debido a problemas con la solicitud del cliente.
- 400 Bad Request: Se emplea cuando la solicitud es inválida o está mal formada. Por ejemplo, cuando faltan datos requeridos o el formato es incorrecto.
from marshmallow import ValidationError
@app.route('/productos', methods=['POST'])
def crear_producto():
datos = request.get_json()
try:
producto = producto_schema.load(datos)
except ValidationError as err:
return jsonify({'error': 'Datos inválidos', 'detalles': err.messages}), 400 # Código 400 Bad Request
# Continuar con la creación del producto...
- 401 Unauthorized: Indica que se requiere autenticación para acceder al recurso. Se utiliza cuando el usuario no ha proporcionado credenciales o estas son inválidas.
- 403 Forbidden: Se utiliza cuando el usuario está autenticado pero no tiene permisos para realizar la operación solicitada.
- 404 Not Found: Significa que el recurso solicitado no existe en el servidor. Es importante devolver este código cuando un recurso no se encuentra, para evitar confusión con otros errores.
@app.route('/articulos/<int:id_articulo>', methods=['GET'])
def obtener_articulo(id_articulo):
articulo = Articulo.query.get(id_articulo)
if not articulo:
return jsonify({'error': 'Artículo no encontrado'}), 404 # Código 404 Not Found
return jsonify(articulo_schema.dump(articulo)), 200
- 405 Method Not Allowed: Se devuelve cuando el método HTTP utilizado no está permitido para la URL solicitada. Flask puede manejar esto automáticamente dependiendo de las rutas definidas.
Códigos de estado para errores del servidor
Los códigos de estado de la serie 5xx indican que ha ocurrido un error en el servidor al procesar la solicitud.
- 500 Internal Server Error: Indica que ha ocurrido un error inesperado en el servidor. Es una buena práctica capturar excepciones y devolver este código cuando no se puede proporcionar más información.
@app.route('/procesar', methods=['POST'])
def procesar():
try:
# Lógica de procesamiento...
except Exception as e:
app.logger.error(f'Error en el servidor: {e}')
return jsonify({'error': 'Error interno del servidor'}), 500 # Código 500 Internal Server Error
- 503 Service Unavailable: Se utiliza cuando el servidor no está disponible temporalmente, por ejemplo, por tareas de mantenimiento.
Buenas prácticas en el uso de códigos de estado
Es esencial seguir ciertas buenas prácticas al utilizar códigos de estado HTTP:
- Consistencia: Utilizar los códigos de estado de manera coherente en toda la API, facilitando a los clientes la interpretación de las respuestas.
- Especificidad: Siempre que sea posible, usar el código que más específicamente describa el resultado de la solicitud, en lugar de recurrir siempre a códigos genéricos.
- Información complementaria: Incluir en el cuerpo de la respuesta información adicional que ayude al cliente a entender el resultado, especialmente en caso de error.
- No abusar de códigos de error: Evitar devolver códigos de estado incorrectos, como usar 200 OK cuando ha ocurrido un error, ya que esto genera confusión y dificulta el manejo de errores en el cliente.
- Documentación: Mantener actualizada la documentación de la API, especificando qué códigos de estado devuelve cada endpoint en diferentes situaciones.
Establecer códigos de estado en Flask
En Flask, además de devolver una tupla con el contenido y el código de estado, también se puede establecer utilizando el atributo status_code
de la respuesta. Por ejemplo:
respuesta = jsonify({'mensaje': 'Operación exitosa'})
respuesta.status_code = 200
return respuesta
Esta forma es útil cuando se necesita manipular la respuesta antes de enviarla.
También es posible definir los códigos de estado en handlers de errores o excepciones personalizadas, facilitando la gestión centralizada de respuestas:
class ExcepcionPersonalizada(Exception):
status_code = 400
def __init__(self, mensaje, status_code=None):
super().__init__(mensaje)
if status_code is not None:
self.status_code = status_code
self.mensaje = mensaje
@app.errorhandler(ExcepcionPersonalizada)
def manejar_excepcion_personalizada(error):
respuesta = jsonify({'error': error.mensaje})
respuesta.status_code = error.status_code
return respuesta
Y lanzar la excepción cuando sea necesario:
@app.route('/accion')
def realizar_accion():
if alguna_condicion_fallida:
raise ExcepcionPersonalizada('Condición no cumplida', status_code=422)
return jsonify({'resultado': 'Acción realizada'}), 200
Códigos de estado personalizados
Aunque HTTP define un conjunto estándar de códigos de estado, en ocasiones puede ser útil utilizar códigos no estándar para situaciones específicas. Sin embargo, es recomendable apegarse a los códigos definidos en la norma RFC 9110, para mantener la compatibilidad y evitar confusiones.
Si se necesita proporcionar información adicional, es preferible utilizar los campos del cuerpo de la respuesta en formato JSON, en lugar de inventar códigos de estado no estándar.
Todas las 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.
Introducción A Flask
Introducción Y Entorno
Instalación Y Configuración Flask Con Venv
Introducción Y Entorno
Mysql Con Sqlalchemy En Flask
Modelos Y Migraciones
Tipos De Datos En Modelos
Modelos Y Migraciones
Operaciones Crud Y Consultas
Modelos Y Migraciones
Asociaciones De Modelos
Modelos Y Migraciones
Migraciones Con Flask-migrate
Modelos Y Migraciones
Rutas Endpoints Rest Get
Api Rest
Respuestas Con Esquemas Flask Marshmallow
Api Rest
Rutas Endpoints Rest Post, Put Y Delete
Api Rest
Manejo De Errores Y Códigos De Estado Http
Api Rest
Autenticación Jwt Con Flask-jwt-extended
Api Rest
Controlador Mvc Con Métodos Get En Flask
Mvc
Sintaxis De Plantillas Jinja 2 En Flask
Mvc
Controlador Mvc Con Métodos Post En Flask
Mvc
Inclusión De Archivos Estáticos En Jinja
Mvc
Validación De Formularios Con Wtforms
Mvc
Subir Archivos En Formularios Jinja En Flask
Mvc
Autenticación Con Flask-login
Mvc
Autorización Con Flask-principal
Mvc
Qué Son Los Blueprints Y Cómo Crear Uno
Blueprints
Integrar Openai Api En Flask Api Rest
Aplicación Con Ia
Sqlalchemy Orm En Flask Mysql
Aplicación Con Ia
Resultados De Ia Con Jinja En Flask
Aplicación Con Ia
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la función del decorador
@app.errorhandler
en Flask. - Implementar respuestas de error estándar en formato JSON.
- Personalizar excepciones para manejar condicionantes específicos.
- Emplear bloques try-except para capturar excepciones inesperadas.
- Configurar y registrar logs de errores en producción.
- Centralizar la gestión de errores creando funciones genéricas.
- Uso adecuado de códigos de estado HTTP en APIs RESTful.