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

Параметри шляху

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

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

Параметри шляху з типами

Ви можете оголосити тип параметра шляху у функції, використовуючи стандартні анотації типів 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 надає вам автоматичний «parsing» запиту.

Валідація даних

Але якщо ви перейдете у браузері за посиланням 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"
    }
  ]
}

тому що параметр шляху item_id мав значення "foo", яке не є int.

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

Примітка

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

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

Це неймовірно корисно під час розробки та налагодження коду, що взаємодіє з вашим 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"}

Порада

Якщо вам цікаво, «AlexNet», «ResNet» та «LeNet» — це просто назви Machine Learning models.

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

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

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

Ви можете повертати елементи enum з вашої операції шляху, навіть вкладені у 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!"
}

Параметри шляху, що містять шляхи

Припустімо, у вас є операція шляху зі шляхом /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, ви отримуєте:

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

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

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