Facilita las notificaciones de errores con HTTPException de FastAPI
¡Muy buenas! En el tutorial de esta semana vamos a aprender en que casos utilizar HTTPException para notificar errores a los usuarios de forma legible cuando utilicen nuestra API hecha con FastAPI.
Si aún no tienes muy claro como funcionan las excepciones en Python, te dejo este tutorial en el que explico su uso desde lo más básico hasta llegar a un nivel profundo 👇
Como utilizar y customizar nuestras propias excepciones con Python
Casos en los que necesitamos enviar errores
Cuando trabajamos en una API hay varios casos en los que necesitaremos enviar un aviso de error a un usuario como por ejemplo:
- Cuando intenta acceder a un recurso que no existe
- No tiene privilegios para realizar una acción
- Nos envía información no válida
- Que en un registro utilice un nombre de usuario o email ya registrado
- etc.
Ventajas de usar HTTPException frente a otro tipo de excepciones en FastAPI
La ventaja de lanzar una excepción de tipo HTTPException frente a otro tipo de excepciones es que FastAPI la capturará y le devolverá al usuario una respuesta en formato JSON con una clave llamada detail en que podremos especificar el error ocurrido.
De esta manera siempre que ocurra un error, el usuario los recibirá con el mismo formato y a nosotros nos facilitará el trabajo.
Ejemplos de uso de HTTPException
Ejemplo 1, usuario no encontrado:
from fastapi import FastAPI
from fastapi import HTTPException
app = FastAPI()
users = [
{
'id': 1,
'name': 'Ichigo'
},
{
'id': 2,
'name': 'Rukia'
},
]
@app.get('/user/{user_id}')
def get_user_by_id(user_id: int):
for user in users:
if user['id'] == user_id:
return user
raise HTTPException(status_code=404, detail='User not found')
En este ejemplo hemos definido una lista con dos diccionarios en los que guardamos los usuarios. Después tenemos un endpoint que recibe el id de usuario en la propia ruta. Recorremos la lista y si encontramos una coincidencia lo retornamos, si no es así lanzamos la HTTPException que recibe como primer parámetro el estado http que queremos dar y en detail un mensaje informativo para el usuario.
Si por ejemplo lanzamos una petición a la ruta /user/3, la respuesta de nuestra API sería:
{
"detail": "User not found"
}
Esto ocurre, ya que no tenemos ningún usuario con el id 3.
Ejemplo 2, token de autenticación inválido:
from fastapi import FastAPI
from fastapi import HTTPException
from fastapi import Query
from fastapi import status
app = FastAPI()
my_token = 'abc123'
@app.get('/items')
def get_items(token: str = Query(...)):
if my_token != token:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail='Invalid token')
return ['item 1', 'item 2', 'item 3']
En este segundo ejemplo he creado un sistema de autenticación muy cutre, pero que nos viene de perlas para el ejemplo. He generado la variable my_token que contiene una cadena alfanumérica y que utilizaremos para compararla por la recibida por parámetro, si coinciden la petición será válida.
En el endpoint vamos a recibir el token vía parámetro query (los parámetros que se envían por la url, en este caso sería: /items?token=abc123) y si coincide con el que tenemos guardado en la variable podrá acceder a la lista de items. Si no es de esta forma, lanzaremos la HTTPException con un mensaje en el que especificamos que el token enviado es inválido.
Si os fijáis, en status_code he usado un módulo de FastAPI llamado status que he importado previamente y en la cual están declaradas una lista de variables con los códigos de estado http más empleados, de esta manera podremos buscar los códigos de http de un modo más sencillo.
Ejemplo 3, controlar excepciones:
from fastapi import FastAPI
from fastapi import HTTPException
from fastapi import status
app = FastAPI()
@app.get('/divide/{num1}/{num2}')
def get_items(num1: int, num2: int):
try:
x = num1 / num2
return {'result': x}
except ZeroDivisionError:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail='You cannot divide a number by 0')
En este ejemplo tenemos un endpoint que recibe dos números y se encarga de dividirlos. Pues bien, si intentamos dividir un número entre 0 se ejecutará la excepción ZeroDivisionError y como la tenemos controlada, podemos lanzar HTTPException avisando al usuario de su error.
¿Qué pasa con las excepciones que no controlamos?
Si en el caso anterior no tuviéramos el bloque try que se encarga de capturar la excepción y el usuario intentase dividir un número entre 0, el mensaje que recibiría el usuario sería "Internal Server Error" en texto plano.
No es muy informativo ¿Verdad? Además, quien sabe cuanto tardaríamos en enterarnos de que existe este error.
Para solucionar esto, FastAPI nos provee de una solución para capturar todas las excepciones no controladas que no evita enviar un mensaje poco informativo al usuario, pero por lo menos sí que tendrá el formato adecuado y al tener controlado por donde pasan esos errores, podremos guardar un log para corregir ese error posteriormente.
Para aprender a utilizar esta solución, os dejo este tutorial donde explico como utilizarla 👇
Cómo capturar las excepciones no controladas con FastAPI
Y bueno, esto es todo por este tutorial, creo que nos hemos metido bastante a fondo, pero si tenéis alguna duda recordad que tenéis la caja de comentarios para preguntar lo que sea que yo os intentaré ayudar 💪.
Espero que este post te ayude y como siempre, te recomiendo seguirme en Twitter para estar al tanto de los nuevo contenido. Ahora también puedes seguirme en Instagram donde estoy subiendo tips, tutoriales en vídeo e información sobre herramientas para developers.
Por último os dejo mi guía para aprender a trabajar con APIs donde explico todo el funcionamiento de una API, el protocolo HTTP y veremos como construir una API con arquitectura REST.
Nos leemos 👋.