You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

9.7 KiB

Manipulação de erros

Há diversas situações em que você precisa notificar um erro a um cliente que está utilizando a sua API.

Esse cliente pode ser um browser com um frontend, o código de outra pessoa, um dispositivo IoT, etc.

Pode ser que você precise comunicar ao cliente que:

  • O cliente não tem privilégios suficientes para aquela operação.
  • O cliente não tem acesso aquele recurso.
  • O item que o cliente estava tentando acessar não existe.
  • etc.

Nesses casos, você normalmente retornaria um HTTP status code na faixa de 400 (do 400 ao 499).

Isso é similar aos status codes HTTP 200 (do 200 ao 299). Esses status codes "200" significam que, de algum modo, houve um "sucesso" na request.

Os status codes na faixa dos 400 significam que houve um erro por parte do cliente.

Você se lembra de todos aqueles erros "404 Not Found" (e piadas)?

Use o HTTPException

Para retornar responses HTTP com erros ao cliente, use o HTTPException.

Importe HTTPException

{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}

Lance uma HTTPException no seu código

HTTPException é uma exceção normal do Python com dados adicionais relevantes para APIs.

Como é uma exceção do Python, você não dá return nela, você dá raise nela.

Isso também significa que, se você está dentro de uma função de utilidade que está chamando dentro da sua função de operação de rota, e lança a HTTPException dentro dessa função de utilidade, o restante do código na função de operação de rota não será executado, a request será encerrada imediatamente e o erro HTTP da HTTPException será enviado ao cliente.

O benefício de lançar uma exceção em vez de retornar um valor ficará mais evidente na seção sobre Dependências e Segurança.

Neste exemplo, quando o cliente solicita um item por um ID que não existe, lance uma exceção com status code 404:

{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}

A response resultante

Se o cliente solicita http://example.com/items/foo (um item_id "foo"), esse cliente receberá um status code HTTP 200 e uma response JSON de:

{
  "item": "The Foo Wrestlers"
}

Mas se o cliente solicita http://example.com/items/bar (um item_id "bar" inexistente), esse cliente receberá um status code HTTP 404 (o erro "not found") e uma response JSON de:

{
  "detail": "Item not found"
}

/// tip | Dica

Ao lançar uma HTTPException, você pode passar qualquer valor que possa ser convertido para JSON como parâmetro detail, não apenas str.

Você pode passar um dict, uma list, etc.

Eles são manipulados automaticamente pelo FastAPI e convertidos para JSON.

///

Adicione headers customizados

Há algumas situações em que é útil poder adicionar headers customizados ao erro HTTP. Por exemplo, para alguns tipos de segurança.

Você provavelmente não precisará usar isso diretamente no seu código.

Mas caso precise em um cenário avançado, você pode adicionar headers customizados:

{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}

Instale manipuladores de exceções customizados

Você pode adicionar manipuladores de exceção customizados com as mesmas utilidades de exceção do Starlette.

Digamos que você tenha uma exceção customizada UnicornException que você (ou uma biblioteca que você usa) possa lançar com raise.

E você quer manipular essa exceção globalmente com o FastAPI.

Você poderia adicionar um manipulador de exceção customizado com @app.exception_handler():

{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}

Aqui, se você fizer uma request para /unicorns/yolo, a operação de rota vai lançar com raise uma UnicornException.

Mas ela será manipulada pelo unicorn_exception_handler.

Assim, você receberá um erro limpo, com um status code HTTP 418 e um conteúdo JSON de:

{"message": "Oops! yolo did something. There goes a rainbow..."}

/// note | Detalhes Técnicos

Você também pode usar from starlette.requests import Request e from starlette.responses import JSONResponse.

FastAPI fornece o mesmo starlette.responses como fastapi.responses apenas como uma conveniência para você, a pessoa desenvolvedora. Mas a maior parte das responses disponíveis vem diretamente do Starlette. O mesmo acontece com Request.

///

Sobrescreva os manipuladores de exceções padrão

FastAPI tem alguns manipuladores padrão de exceções.

Esses manipuladores são responsáveis por retornar as responses JSON padrão quando você lança com raise uma HTTPException e quando a request tem dados inválidos.

Você pode sobrescrever esses manipuladores de exceção com os seus próprios.

Sobrescreva exceções de validação da request

Quando uma request contém dados inválidos, FastAPI internamente lança um RequestValidationError.

E também inclui um manipulador de exceções padrão para ele.

Para sobrescrevê-lo, importe o RequestValidationError e use-o com @app.exception_handler(RequestValidationError) para decorar o manipulador de exceções.

O manipulador de exceções receberá um Request e a exceção.

{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}

Agora, se você for para /items/foo, em vez de receber o erro JSON padrão com:

{
    "detail": [
        {
            "loc": [
                "path",
                "item_id"
            ],
            "msg": "value is not a valid integer",
            "type": "type_error.integer"
        }
    ]
}

você receberá uma versão em texto, com:

Validation errors:
Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer

Sobrescreva o manipulador de erro HTTPException

Do mesmo modo, você pode sobrescrever o manipulador de HTTPException.

Por exemplo, você poderia querer retornar uma response em texto simples em vez de JSON para estes erros:

{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}

/// note | Detalhes Técnicos

Você também pode usar from starlette.responses import PlainTextResponse.

FastAPI fornece o mesmo starlette.responses como fastapi.responses apenas como uma conveniência para você, a pessoa desenvolvedora. Mas a maior parte das responses disponíveis vem diretamente do Starlette.

///

/// warning | Atenção

Tenha em mente que o RequestValidationError contém as informações do nome do arquivo e da linha onde o erro de validação acontece, para que você possa mostrá-las nos seus logs com as informações relevantes, se quiser.

Mas isso significa que, se você simplesmente convertê-lo para uma string e retornar essa informação diretamente, você poderia acabar vazando um pouco de informação sobre o seu sistema; por isso, aqui o código extrai e mostra cada erro de forma independente.

///

Use o body do RequestValidationError

O RequestValidationError contém o body que ele recebeu com dados inválidos.

Você poderia usá-lo enquanto desenvolve sua aplicação para registrar o body e depurá-lo, retorná-lo ao usuário, etc.

{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}

Agora tente enviar um item inválido como:

{
  "title": "towel",
  "size": "XL"
}

Você receberá uma response dizendo que os dados são inválidos contendo o body recebido:

{
  "detail": [
    {
      "loc": [
        "body",
        "size"
      ],
      "msg": "value is not a valid integer",
      "type": "type_error.integer"
    }
  ],
  "body": {
    "title": "towel",
    "size": "XL"
  }
}

HTTPException do FastAPI vs HTTPException do Starlette

FastAPI tem a sua própria HTTPException.

E a classe de erro HTTPException do FastAPI herda da classe de erro HTTPException do Starlette.

A única diferença é que a HTTPException do FastAPI aceita qualquer dado que possa ser convertido para JSON no campo detail, enquanto a HTTPException do Starlette aceita apenas strings para ele.

Portanto, você pode continuar lançando a HTTPException do FastAPI normalmente no seu código.

Mas quando registrar um manipulador de exceção, você deveria registrá-lo para a HTTPException do Starlette.

Dessa forma, se qualquer parte do código interno do Starlette, ou uma extensão ou plug-in do Starlette, lançar uma HTTPException do Starlette, seu manipulador poderá capturá-la e tratá-la.

Neste exemplo, para poder ter ambas as HTTPExceptions no mesmo código, a exceção do Starlette é renomeada para StarletteHTTPException:

from starlette.exceptions import HTTPException as StarletteHTTPException

Reutilize os manipuladores de exceção do FastAPI

Se você quiser usar a exceção junto com os mesmos manipuladores de exceção padrão do FastAPI, você pode importar e reutilizar os manipuladores de exceção padrão de fastapi.exception_handlers:

{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}

Neste exemplo, você está apenas imprimindo o erro com uma mensagem muito expressiva, mas a ideia é essa. Você pode usar a exceção e então simplesmente reutilizar os manipuladores de exceção padrão.