Ir para o conteúdo

Parâmetros de path

Você pode declarar "parâmetros" ou "variáveis" de path com a mesma sintaxe usada por strings de formatação do Python:

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/{item_id}")
async def read_item(item_id):
    return {"item_id": item_id}

O valor do parâmetro de path item_id será passado para a sua função como o argumento item_id.

Então, se você executar este exemplo e acessar http://127.0.0.1:8000/items/foo, você verá uma resposta:

{"item_id":"foo"}

Parâmetros de path com tipos

Você pode declarar o tipo de um parâmetro de path na função, usando as anotações de tipo padrão do Python:

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

Neste caso, item_id é declarado como um int.

Verifique

Isso fornecerá suporte do editor dentro da sua função, com verificações de erros, preenchimento automático, etc.

Dados conversão

Se você executar este exemplo e abrir seu navegador em http://127.0.0.1:8000/items/3, você verá uma resposta:

{"item_id":3}

Verifique

Perceba que o valor que sua função recebeu (e retornou) é 3, como um int do Python, não uma string "3".

Então, com essa declaração de tipo, o FastAPI fornece "parsing" automático do request.

Validação de dados

Mas se você for no navegador para http://127.0.0.1:8000/items/foo, verá um bom erro HTTP:

{
  "detail": [
    {
      "type": "int_parsing",
      "loc": [
        "path",
        "item_id"
      ],
      "msg": "Input should be a valid integer, unable to parse string as an integer",
      "input": "foo"
    }
  ]
}

porque o parâmetro de path item_id tinha o valor "foo", que não é um int.

O mesmo erro apareceria se você fornecesse um float em vez de um int, como em: http://127.0.0.1:8000/items/4.2

Verifique

Então, com a mesma declaração de tipo do Python, o FastAPI fornece validação de dados.

Observe que o erro também declara claramente exatamente o ponto onde a validação não passou.

Isso é incrivelmente útil ao desenvolver e depurar código que interage com sua API.

Documentação

E quando você abrir seu navegador em http://127.0.0.1:8000/docs, você verá documentação automática, interativa, da API como:

Verifique

Novamente, apenas com a mesma declaração de tipo do Python, o FastAPI fornece documentação automática e interativa (integrando o Swagger UI).

Observe que o parâmetro de path está declarado como um inteiro.

Benefícios baseados em padrões, documentação alternativa

E como o schema gerado é do padrão OpenAPI, existem muitas ferramentas compatíveis.

Por causa disso, o próprio FastAPI fornece uma documentação alternativa da API (usando ReDoc), que você pode acessar em http://127.0.0.1:8000/redoc:

Da mesma forma, existem muitas ferramentas compatíveis. Incluindo ferramentas de geração de código para muitas linguagens.

Pydantic

Toda a validação de dados é realizada nos bastidores pelo Pydantic, então você recebe todos os benefícios disso. E você sabe que está em boas mãos.

Você pode usar as mesmas declarações de tipo com str, float, bool e muitos outros tipos de dados complexos.

Vários deles são explorados nos próximos capítulos do tutorial.

A ordem importa

Ao criar operações de rota, você pode encontrar situações em que tem um path fixo.

Como /users/me, digamos que seja para obter dados sobre o usuário atual.

E então você também pode ter um path /users/{user_id} para obter dados sobre um usuário específico por algum ID de usuário.

Como as operações de rota são avaliadas em ordem, você precisa garantir que o path para /users/me seja declarado antes do de /users/{user_id}:

from fastapi import FastAPI

app = FastAPI()


@app.get("/users/me")
async def read_user_me():
    return {"user_id": "the current user"}


@app.get("/users/{user_id}")
async def read_user(user_id: str):
    return {"user_id": user_id}

Caso contrário, o path para /users/{user_id} também corresponderia a /users/me, "achando" que está recebendo um parâmetro user_id com o valor "me".

Da mesma forma, você não pode redefinir uma operação de rota:

from fastapi import FastAPI

app = FastAPI()


@app.get("/users")
async def read_users():
    return ["Rick", "Morty"]


@app.get("/users")
async def read_users2():
    return ["Bean", "Elfo"]

A primeira sempre será usada, já que o path corresponde primeiro.

Valores predefinidos

Se você tem uma operação de rota que recebe um parâmetro de path, mas quer que os valores válidos possíveis do parâmetro de path sejam predefinidos, você pode usar um Enum padrão do Python.

Crie uma classe Enum

Importe Enum e crie uma subclasse que herde de str e de Enum.

Ao herdar de str, a documentação da API saberá que os valores devem ser do tipo string e poderá renderizá-los corretamente.

Em seguida, crie atributos de classe com valores fixos, que serão os valores válidos disponíveis:

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

Informação

Enumerations (ou enums) estão disponíveis no Python desde a versão 3.4.

Dica

Se você está se perguntando, "AlexNet", "ResNet" e "LeNet" são apenas nomes de modelos de Aprendizado de Máquina.

Declare um parâmetro de path

Em seguida, crie um parâmetro de path com anotação de tipo usando a classe enum que você criou (ModelName):

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

Verifique a documentação

Como os valores disponíveis para o parâmetro de path são predefinidos, a documentação interativa pode mostrá-los de forma agradável:

Trabalhando com enumerações do Python

O valor do parâmetro de path será um membro de enumeração.

Compare membros de enumeração

Você pode compará-lo com o membro de enumeração no seu enum ModelName criado:

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

Obtenha o valor da enumeração

Você pode obter o valor real (um str neste caso) usando model_name.value, ou, em geral, your_enum_member.value:

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

Dica

Você também pode acessar o valor "lenet" com ModelName.lenet.value.

Retorne membros de enumeração

Você pode retornar membros de enum da sua operação de rota, até mesmo aninhados em um corpo JSON (por exemplo, um dict).

Eles serão convertidos para seus valores correspondentes (strings neste caso) antes de serem retornados ao cliente:

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

No seu cliente, você receberá uma resposta JSON como:

{
  "model_name": "alexnet",
  "message": "Deep Learning FTW!"
}

Parâmetros de path que contêm paths

Digamos que você tenha uma operação de rota com um path /files/{file_path}.

Mas você precisa que o próprio file_path contenha um path, como home/johndoe/myfile.txt.

Então, a URL para esse arquivo seria algo como: /files/home/johndoe/myfile.txt.

Suporte do OpenAPI

O OpenAPI não oferece suporte a uma maneira de declarar um parâmetro de path que contenha um path dentro, pois isso poderia levar a cenários difíceis de testar e definir.

Ainda assim, você pode fazer isso no FastAPI, usando uma das ferramentas internas do Starlette.

E a documentação continuará funcionando, embora não adicione nenhuma informação dizendo que o parâmetro deve conter um path.

Conversor de path

Usando uma opção diretamente do Starlette você pode declarar um parâmetro de path contendo um path usando uma URL como:

/files/{file_path:path}

Nesse caso, o nome do parâmetro é file_path, e a última parte, :path, diz que o parâmetro deve corresponder a qualquer path.

Então, você pode usá-lo com:

from fastapi import FastAPI

app = FastAPI()


@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
    return {"file_path": file_path}

Dica

Você pode precisar que o parâmetro contenha /home/johndoe/myfile.txt, com uma barra inicial (/).

Nesse caso, a URL seria: /files//home/johndoe/myfile.txt, com uma barra dupla (//) entre files e home.

Recapitulação

Com o FastAPI, ao usar declarações de tipo do Python curtas, intuitivas e padrão, você obtém:

  • Suporte no editor: verificações de erro, autocompletar, etc.
  • "Parsing" de dados
  • Validação de dados
  • Anotação da API e documentação automática

E você só precisa declará-los uma vez.

Essa é provavelmente a principal vantagem visível do FastAPI em comparação com frameworks alternativos (além do desempenho bruto).