Erros HTTP
Quando as coisas dão errado precisamos informar o cliente HTTP com o código de status correto, erros podem acontecer quando ocorre uma falha inesperada no servidor, quando o recurdo solicitado não existe ou quando o cliente efetua uma requisição inválida.
Vamos tentar duplicar a criação de um usuário fazendo novamente a mesma chamada POST e a mensagem que receberemos é:
Faça essa chamada mais de 1x:
curl -X 'POST' -H 'Content-Type: application/json' \
--data-raw '{"email": "pam@dm.com", "dept": "Accounting", "password": "jimjim", "name": "Pam Besly"}' \
-k 'http://localhost:8000/user/'
HTTP/1.1 500 Internal Server Error
Internal Server Error
A mensagem de erro não ajuda muito a sabermos o que ocorreu de fato e portanto podemos curtomizar este comportamento.
Quando temos este caso expecifico o código de erro correto é o 409 Conflict
que innforma que o estado interno está em conflito com o estado que está sendo enviado no request, ou seja, estamos tentando criar um usuário que já existe.
Para customizar este comportamento podemos editar o arquivo routes/user.py
# No topo
from sqlalchemy.exc import IntegrityError
# Na função `create_user`
async def create_user(.......):
...
try:
session.commit()
except IntegrityError:
raise HTTPException(status_code=409, detail="User already exists")
A exception IntegrityError
será levantada para qualquer problemas encontrado no banco de dados portanto não é ainda a melhor opção, precisamos ser mais especificos para ajudar quem está usando a API, portanto vamos fazer as seguintes modificações:
- Continuar tratando a IntegrityError porém com o código 500 e mensagem de erro genérica.
- Adicionar um guard para garantir que o usuário a ser criado não existe.
@router.post(
"/", response_model=UserResponse, status_code=201, dependencies=[SuperUser]
)
async def create_user(*, session: Session = ActiveSession, user: UserRequest):
"""Creates new user"""
if session.exec(select(User).where(User.username == user.username)).first():
raise HTTPException(status_code=409, detail="Username already taken")
db_user = User.from_orm(user) # transform UserRequest in User
session.add(db_user)
try:
session.commit()
except IntegrityError:
raise HTTPException(status_code=500, detail="Database IntegrityError")
session.refresh(db_user)
return db_user
E agora sim teremos o retorno esperado
HTTP/1.1 409 Conflict
Username already taken
E no caso de um outro erro de integridade ai invés de mostrar apenas o erro 500 genérico informamos especificamente que se trata de um problema no banco de dados, porém sem expor o erro diretamente.
Uma boa prática seria colocar um logger ou um analisador de exceptions como o NewRelic ou o Sentry, faremos isso em outra parte do treinamento.
Vamos agora continuar implementando as rotas de usuário -->