Перейти до змісту

Path Параметри

Ви можете визначити "параметри" або "змінні" шляху, використовуючи синтаксис форматованих рядків:

from fastapi import FastAPI

app = FastAPI()


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

Значення параметра шляху item_id передається у функцію як аргумент item_id.

Якщо запустити цей приклад та перейти за посиланням http://127.0.0.1:8000/items/foo, то отримаємо таку відповідь:

{"item_id":"foo"}

Path параметри з типами

Ви можете визначити тип параметра шляху у функції, використовуючи стандартні анотації типів Python:

from fastapi import FastAPI

app = FastAPI()


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

У такому випадку item_id визначається як int.

Примітка

Це дасть можливість підтримки редактора всередині функції з перевірками помилок, автодоповнення тощо.

Перетворення даних

Якщо запустити цей приклад і перейти за посиланням http://127.0.0.1:8000/items/3, то отримаєте таку відповідь:

{"item_id":3}

Примітка

Зверніть увагу, що значення, яке отримала (і повернула) ваша функція, — це 3. Це Python int, а не рядок "3".

Отже, з таким оголошенням типу FastAPI автоматично виконує "парсинг" запитів.

Перевірка даних

Якщо ж відкрити у браузері посилання http://127.0.0.1:8000/items/foo, то побачимо цікаву 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",
      "url": "https://errors.pydantic.dev/2.1/v/int_parsing"
    }
  ]
}
тому що параметр шляху має значення "foo", яке не є типом int.

Таку саму помилку отримаємо, якщо передати float замість int, як бачимо, у цьому прикладі: http://127.0.0.1:8000/items/4.2

Примітка

Отже, FastAPI надає перевірку типів з таким самим оголошенням типу в Python.

Зверніть увагу, що помилка також чітко вказує саме на те місце, де валідація не пройшла.

Це неймовірно корисно під час розробки та дебагінгу коду, що взаємодіє з вашим API.

Документація

Тепер коли відкриєте свій браузер за посиланням http://127.0.0.1:8000/docs, то побачите автоматично згенеровану, інтерактивну API-документацію:

Примітка

Знову ж таки, лише з цим самим оголошенням типу в Python, FastAPI надає вам автоматичну, інтерактивну документацію (з інтеграцією Swagger UI).

Зверніть увагу, що параметр шляху оголошений як ціле число.

Переваги стандартизації, альтернативна документація

І оскільки згенерована схема відповідає стандарту OpenAPI, існує багато сумісних інструментів.

З цієї причини FastAPI також надає альтернативну документацію API (використовуючи ReDoc), до якої можна отримати доступ за посиланням http://127.0.0.1:8000/redoc:

Таким чином, існує багато сумісних інструментів, включаючи інструменти для генерації коду для багатьох мов.

Pydantic

Вся валідація даних виконується за лаштунками за допомогою Pydantic, тому Ви отримуєте всі переваги від його використання. І можете бути впевнені, що все в надійних руках.

Ви можете використовувати ті самі оголошення типів з str, float, bool та багатьма іншими складними типами даних.

Декілька з них будуть розглянуті в наступних розділах посібника.

Порядок має значення

При створенні операцій шляху можуть виникати ситуації, коли шлях фіксований.

Наприклад, /users/me. Припустимо, що це шлях для отримання даних про поточного користувача.

А також у вас може бути шлях /users/{user_id}, щоб отримати дані про конкретного користувача за його ID.

Оскільки операції шляху оцінюються по черзі, Ви повинні переконатися, що шлях для /users/me оголошений перед шляхом для /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}

Інакше шлях для /users/{user_id} також буде відповідати для /users/me, "вважаючи", що він отримує параметр user_id зі значенням "me".

Аналогічно, Ви не можете оголосити операцію шляху:

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"]

Перша операція буде завжди використовуватися, оскільки шлях збігається першим.

Попередньо визначені значення

Якщо у вас є операція шляху, яка приймає параметр шляху, але Ви хочете, щоб можливі допустимі значення параметра шляху були попередньо визначені, Ви можете використати стандартний Python Enum.

Створення класу Enum

Імпортуйте Enum і створіть підклас, що наслідується від str та Enum.

Наслідуючи від str, документація API зможе визначити, що значення повинні бути типу string, і правильно їх відобразить.

Після цього створіть атрибути класу з фіксованими значеннями, які будуть доступними допустимими значеннями:

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"}

Додаткова інформація

Перелічення (або enums) доступні в Python починаючи з версії 3.4.

Порада

Якщо вам цікаво, "AlexNet", "ResNet" та "LeNet" — це просто назви ML моделей Machine Learning.

Оголосіть параметр шляху

Потім створіть параметр шляху з анотацією типу, використовуючи створений вами клас enum (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"}

Перевірка документації

Оскільки доступні значення для параметра шляху визначені заздалегідь, інтерактивна документація зможе красиво їх відобразити:

Робота з перелічуваннями у Python

Значення параметра шляху буде елементом перелічування.

Порівняння елементів перелічування

Ви можете порівнювати його з елементами перелічування у створеному вами enum 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"}

Отримання значення перелічування

Ви можете отримати фактичне значення (у цьому випадку це str), використовуючи model_name.value, або загалом 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"}

Порада

Ви також можете отримати доступ до значення "lenet", використовуючи ModelName.lenet.value.

Повернення елементів перелічування

Ви можете повертати елементи перелічування з вашої операції шляху, навіть вкладені у JSON-тіло (наприклад, dict).

Вони будуть перетворені на відповідні значення (у цьому випадку рядки) перед поверненням клієнту:

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"}

На стороні клієнта Ви отримаєте відповідь у форматі JSON, наприклад:

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

Path-параметри, що містять шляхи

Припустимо, у вас є операція шляху з маршрутом /files/{file_path}.

Але вам потрібно, щоб file_path містив шлях, наприклад home/johndoe/myfile.txt.

Отже, URL для цього файлу виглядатиме так: /files/home/johndoe/myfile.txt.

Підтримка OpenAPI

OpenAPI не підтримує спосіб оголошення параметра шляху, що містить шлях всередині, оскільки це може призвести до сценаріїв, які складно тестувати та визначати.

Однак (одначе), Ви все одно можете зробити це в FastAPI, використовуючи один із внутрішніх інструментів Starlette.

Документація все ще працюватиме, хоча й не додаватиме опису про те, що параметр повинен містити шлях.

Конвертер шляху

Використовуючи опцію безпосередньо зі Starlette, Ви можете оголосити параметр шляху, що містить шлях, використовуючи URL на кшталт:

/files/{file_path:path}
У цьому випадку ім'я параметра — file_path, а остання частина :path вказує на те, що параметр повинен відповідати будь-якому шляху.

Отже, Ви можете використати його так:

from fastapi import FastAPI

app = FastAPI()


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

Порада

Вам може знадобитися, щоб параметр містив /home/johndoe/myfile.txt із початковою косою рискою (/).

У такому випадку URL виглядатиме так: /files//home/johndoe/myfile.txt, із подвійною косою рискою (//) між files і home.

Підсумок

З FastAPI, використовуючи короткі, інтуїтивно зрозумілі та стандартні оголошення типів Python, Ви отримуєте:

  • Підтримку в редакторі: перевірка помилок, автодоповнення тощо.
  • "Парсинг" даних
  • Валідацію даних
  • Анотацію API та автоматичну документацію

І вам потрібно оголосити їх лише один раз.

Це, ймовірно, основна видима перевага FastAPI порівняно з альтернативними фреймворками (окрім високої продуктивності).