Path-параметры¶
Вы можете определить "параметры" или "переменные" пути, используя синтаксис форматированных строк 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 выполняет автоматический "парсинг" запросов.
Проверка данных¶
Если откроете браузер по адресу http://127.0.0.1:8000/items/foo, то увидите интересную HTTP-ошибку:
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
из-за того, что параметр пути item_id
имеет значение "foo"
, которое не является типом int
.
Та же ошибка возникнет, если вместо int
передать float
, например: http://127.0.0.1:8000/items/4.2
"Заметка"
FastAPI обеспечивает проверку типов, используя всё те же определения типов.
Обратите внимание, что в тексте ошибки явно указано место не прошедшее проверку.
Это очень полезно при разработке и отладке кода, который взаимодействует с API.
Документация¶
И теперь, когда откроете браузер по адресу: http://127.0.0.1:8000/docs, то увидите вот такую автоматически сгенерированную документацию API:
"Заметка"
Ещё раз, просто используя определения типов, 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"]
Первый будет выполняться всегда, так как путь совпадает первым.
Предопределенные значения¶
Что если нам нужно заранее определить допустимые параметры пути, которые операция пути может принимать? В таком случае можно использовать стандартное перечисление Enum
Python.
Создание класса 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"}
"Дополнительная информация"
Перечисления (enum) доступны в Python начиная с версии 3.4.
"Подсказка"
Если интересно, то "AlexNet", "ResNet" и "LeNet" - это названия моделей машинного обучения.
Определение параметра пути¶
Определите параметр пути, используя в аннотации типа класс перечисления (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¶
Значение параметра пути будет элементом перечисления.
Сравнение элементов перечисления¶
Вы можете сравнить это значение с элементом перечисления класса 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"}
{
"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 по сравнению с альтернативными фреймворками (кроме сырой производительности).