Flask
Tutorial Flask: Manejo de errores y excepciones
Aprende a gestionar errores y excepciones en Flask con respuestas JSON estructuradas para APIs REST profesionales y robustas.
Aprende Flask y certifícate@app.errorhandler()
Flask proporciona el decorador @app.errorhandler() para capturar y manejar errores específicos que ocurren durante el procesamiento de peticiones HTTP. Este mecanismo permite interceptar excepciones antes de que lleguen al cliente y devolver respuestas personalizadas en lugar de los mensajes de error genéricos del servidor.
El decorador @app.errorhandler() acepta como parámetro el código de estado HTTP o la clase de excepción que queremos capturar. Cuando se produce un error que coincide con el tipo especificado, Flask ejecuta automáticamente la función decorada en lugar de mostrar la página de error predeterminada.
from flask import Flask, jsonify
app = Flask(__name__)
@app.errorhandler(404)
def not_found(error):
return jsonify({
'error': 'Recurso no encontrado',
'message': 'La URL solicitada no existe en el servidor'
}), 404
Manejo de errores HTTP comunes
Los códigos de estado HTTP más frecuentes en APIs REST requieren un tratamiento específico para proporcionar información útil al cliente. Cada error debe incluir un mensaje descriptivo y mantener la consistencia en el formato de respuesta.
@app.errorhandler(400)
def bad_request(error):
return jsonify({
'error': 'Petición incorrecta',
'message': 'Los datos enviados no son válidos'
}), 400
@app.errorhandler(405)
def method_not_allowed(error):
return jsonify({
'error': 'Método no permitido',
'message': 'El método HTTP utilizado no está permitido para este endpoint'
}), 405
@app.errorhandler(500)
def internal_error(error):
return jsonify({
'error': 'Error interno del servidor',
'message': 'Ha ocurrido un error inesperado'
}), 500
Captura de excepciones personalizadas
Además de los códigos HTTP, @app.errorhandler() puede capturar excepciones específicas de Python. Esto resulta especialmente útil para manejar errores de validación o lógica de negocio de manera centralizada.
@app.errorhandler(ValueError)
def handle_value_error(error):
return jsonify({
'error': 'Valor inválido',
'message': str(error)
}), 400
@app.errorhandler(KeyError)
def handle_key_error(error):
return jsonify({
'error': 'Campo requerido',
'message': f'El campo {str(error)} es obligatorio'
}), 400
Un ejemplo práctico muestra cómo estas excepciones personalizadas se integran con los endpoints de la API:
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.get_json()
# Estas excepciones serán capturadas por los errorhandlers
if not data.get('email'):
raise KeyError('email')
if len(data.get('password', '')) < 8:
raise ValueError('La contraseña debe tener al menos 8 caracteres')
return jsonify({'message': 'Usuario creado correctamente'}), 201
Manejo global de errores JSON
Para APIs que trabajan exclusivamente con formato JSON, es recomendable crear un manejador que capture cualquier error no específicamente definido y lo formatee de manera consistente:
@app.errorhandler(Exception)
def handle_exception(error):
# Si es un error HTTP conocido, mantener su código
if hasattr(error, 'code'):
return jsonify({
'error': error.name,
'message': error.description
}), error.code
# Para errores no HTTP, devolver 500
return jsonify({
'error': 'Error interno',
'message': 'Ha ocurrido un error inesperado'
}), 500
Acceso a información del error
Los manejadores de errores reciben como parámetro un objeto que contiene información detallada sobre el error ocurrido. Este objeto permite acceder a propiedades útiles para personalizar la respuesta:
@app.errorhandler(404)
def not_found(error):
return jsonify({
'error': 'Recurso no encontrado',
'message': error.description,
'path': request.path,
'method': request.method
}), 404
La información del contexto como la ruta solicitada y el método HTTP ayuda a los desarrolladores a identificar rápidamente la causa del error durante el desarrollo y depuración de la API.
Respuestas de error estructuradas
Una API REST profesional requiere respuestas de error consistentes y bien estructuradas que faciliten tanto el desarrollo como la depuración. La estandarización del formato de errores permite a los clientes de la API procesar las respuestas de manera predecible, independientemente del tipo de error que se produzca.
Estructura base para respuestas de error
El diseño de una estructura de error estándar debe incluir campos que proporcionen información suficiente para identificar y resolver el problema. Una estructura típica contiene el código de error, mensaje descriptivo, detalles adicionales y metadatos útiles para el contexto:
from flask import Flask, jsonify, request
from datetime import datetime
app = Flask(__name__)
def create_error_response(error_code, message, details=None, status_code=400):
"""Crea una respuesta de error estructurada y consistente"""
error_response = {
'success': False,
'error': {
'code': error_code,
'message': message,
'timestamp': datetime.utcnow().isoformat() + 'Z'
}
}
if details:
error_response['error']['details'] = details
return jsonify(error_response), status_code
Implementación de errores de validación
Los errores de validación requieren un tratamiento especial ya que pueden incluir múltiples campos con problemas específicos. Una estructura detallada permite al cliente identificar exactamente qué datos necesitan corrección:
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.get_json()
validation_errors = []
# Validación de campos requeridos
required_fields = ['name', 'email', 'password']
for field in required_fields:
if not data.get(field):
validation_errors.append({
'field': field,
'message': f'El campo {field} es obligatorio'
})
# Validación de formato de email
if data.get('email') and '@' not in data['email']:
validation_errors.append({
'field': 'email',
'message': 'El formato del email no es válido'
})
# Validación de longitud de contraseña
if data.get('password') and len(data['password']) < 8:
validation_errors.append({
'field': 'password',
'message': 'La contraseña debe tener al menos 8 caracteres'
})
if validation_errors:
return create_error_response(
'VALIDATION_ERROR',
'Los datos proporcionados no son válidos',
{'fields': validation_errors},
400
)
return jsonify({'success': True, 'message': 'Usuario creado correctamente'}), 201
Categorización de errores por tipo
La categorización de errores facilita el manejo programático en el lado del cliente. Cada categoría puede tener un prefijo específico que identifique el tipo de problema:
# Errores de autenticación
def create_auth_error(message, details=None):
return create_error_response('AUTH_ERROR', message, details, 401)
# Errores de autorización
def create_permission_error(message, details=None):
return create_error_response('PERMISSION_ERROR', message, details, 403)
# Errores de recursos no encontrados
def create_not_found_error(resource_type, resource_id=None):
message = f'{resource_type} no encontrado'
details = {'resource_type': resource_type}
if resource_id:
details['resource_id'] = resource_id
return create_error_response('RESOURCE_NOT_FOUND', message, details, 404)
# Errores de conflicto
def create_conflict_error(message, details=None):
return create_error_response('CONFLICT_ERROR', message, details, 409)
Manejo de errores con contexto de petición
Las respuestas estructuradas pueden enriquecerse con información del contexto de la petición HTTP, proporcionando datos adicionales que faciliten la depuración:
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
# Simulación de búsqueda de usuario
users = [{'id': 1, 'name': 'Juan'}, {'id': 2, 'name': 'María'}]
user = next((u for u in users if u['id'] == user_id), None)
if not user:
return jsonify({
'success': False,
'error': {
'code': 'USER_NOT_FOUND',
'message': 'El usuario solicitado no existe',
'timestamp': datetime.utcnow().isoformat() + 'Z',
'request_info': {
'method': request.method,
'path': request.path,
'user_id': user_id
}
}
}), 404
return jsonify({'success': True, 'data': user})
Respuestas de error para operaciones batch
Cuando una operación procesa múltiples elementos, la estructura de error debe reflejar tanto los éxitos como los fallos de manera granular:
@app.route('/api/users/batch', methods=['POST'])
def create_users_batch():
data = request.get_json()
users_data = data.get('users', [])
results = []
errors = []
for index, user_data in enumerate(users_data):
if not user_data.get('email'):
errors.append({
'index': index,
'field': 'email',
'message': 'Email es obligatorio',
'data': user_data
})
elif '@' not in user_data['email']:
errors.append({
'index': index,
'field': 'email',
'message': 'Formato de email inválido',
'data': user_data
})
else:
results.append({
'index': index,
'status': 'created',
'data': user_data
})
if errors:
return jsonify({
'success': False,
'error': {
'code': 'BATCH_VALIDATION_ERROR',
'message': f'{len(errors)} elementos contienen errores',
'timestamp': datetime.utcnow().isoformat() + 'Z',
'details': {
'total_items': len(users_data),
'successful_items': len(results),
'failed_items': len(errors),
'errors': errors
}
}
}), 400
return jsonify({
'success': True,
'message': f'{len(results)} usuarios creados correctamente',
'data': results
}), 201
Integración con manejadores de errores globales
Los manejadores de errores globales pueden utilizar la estructura estandarizada para mantener la consistencia en toda la aplicación:
@app.errorhandler(404)
def handle_not_found(error):
return create_error_response(
'ENDPOINT_NOT_FOUND',
'El endpoint solicitado no existe',
{
'available_methods': ['GET', 'POST'],
'suggested_endpoints': ['/api/users', '/api/users/<id>']
},
404
)
@app.errorhandler(405)
def handle_method_not_allowed(error):
return create_error_response(
'METHOD_NOT_ALLOWED',
'Método HTTP no permitido para este endpoint',
{
'method_used': request.method,
'allowed_methods': error.valid_methods if hasattr(error, 'valid_methods') else []
},
405
)
Esta aproximación estructurada garantiza que todos los errores de la API mantengan un formato consistente, facilitando tanto el desarrollo del cliente como el mantenimiento del código del servidor.
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.
Introducción A Flask
Introducción Y Entorno
Instalación Y Configuración Flask Con Venv
Introducción Y Entorno
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
Ejercicios de programación de Flask
Evalúa tus conocimientos de esta lección Manejo de errores y excepciones con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.