# Path-параметры { #path-parameters }

Вы можете определить "параметры" или "переменные" пути, используя синтаксис форматированных строк Python:

{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}

Значение параметра пути `item_id` будет передано в функцию в качестве аргумента `item_id`.

Если запустите этот пример и перейдёте по адресу: <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, то увидите ответ:

```JSON
{"item_id":"foo"}
```

## Параметры пути с типами { #path-parameters-with-types }

Вы можете объявить тип параметра пути в функции, используя стандартные аннотации типов Python:

{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}

Здесь, `item_id` объявлен типом `int`.

/// check | Заметка

Это обеспечит поддержку редактора кода внутри функции (проверка ошибок, автозавершение и т.п.).

///

## <abbr title="также известное как: сериализация, парсинг, маршаллинг">Преобразование</abbr> данных { #data-conversion }

Если запустите этот пример и перейдёте по адресу: <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>, то увидите ответ:

```JSON
{"item_id":3}
```

/// check | Заметка

Обратите внимание на значение `3`, которое получила (и вернула) функция. Это целочисленный Python `int`, а не строка `"3"`.

Используя такое объявление типов, **FastAPI** выполняет автоматический <abbr title="преобразование строк из HTTP-запроса в типы данных Python">"парсинг"</abbr> запросов.

///

## Валидация данных { #data-validation }

Если откроете браузер по адресу <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, то увидите интересную HTTP-ошибку:

```JSON
{
  "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`.

Та же ошибка возникнет, если вместо `int` передать `float`, например: <a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a>

/// check | Заметка

**FastAPI** обеспечивает валидацию данных, используя всё те же определения типов.

Обратите внимание, что в тексте ошибки явно указано место, не прошедшее проверку.

Это очень полезно при разработке и отладке кода, который взаимодействует с API.

///

## Документация { #documentation }

И теперь, когда откроете браузер по адресу: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, то увидите вот такую автоматически сгенерированную документацию API:

<img src="/img/tutorial/path-params/image01.png">

/// check | Заметка

Ещё раз, просто используя определения типов, **FastAPI** обеспечивает автоматическую интерактивную документацию (с интеграцией Swagger UI).

Обратите внимание, что параметр пути объявлен целочисленным.

///

## Преимущества стандартизации, альтернативная документация { #standards-based-benefits-alternative-documentation }

Поскольку сгенерированная схема соответствует стандарту <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md" class="external-link" target="_blank">OpenAPI</a>, её можно использовать со множеством совместимых инструментов.

Именно поэтому, **FastAPI** сам предоставляет альтернативную документацию API (используя ReDoc), которую можно получить по адресу: <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.

<img src="/img/tutorial/path-params/image02.png">

По той же причине, есть множество совместимых инструментов, включая инструменты генерации кода для многих языков.

## Pydantic { #pydantic }

Вся проверка данных выполняется под капотом с помощью <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>. Поэтому вы можете быть уверены в качестве обработки данных.

Вы можете использовать в аннотациях как простые типы данных, вроде `str`, `float`, `bool`, так и более сложные типы.

Некоторые из них рассматриваются в следующих главах данного руководства.

## Порядок имеет значение { #order-matters }

При создании *операций пути* можно столкнуться с ситуацией, когда путь является фиксированным.

Например, `/users/me`. Предположим, что это путь для получения данных о текущем пользователе.

У вас также может быть путь `/users/{user_id}`, чтобы получить данные о конкретном пользователе по его ID.

Поскольку *операции пути* выполняются в порядке их объявления, необходимо, чтобы путь для `/users/me` был объявлен раньше, чем путь для `/users/{user_id}`:

{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}

Иначе путь для `/users/{user_id}` также будет соответствовать `/users/me`, "подразумевая", что он получает параметр `user_id` со значением `"me"`.

Аналогично, вы не можете переопределить операцию с путем:

{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}

Первый будет выполняться всегда, так как путь совпадает первым.

## Предопределенные значения { #predefined-values }

Что если нам нужно заранее определить допустимые *параметры пути*, которые *операция пути* может принимать? В таком случае можно использовать стандартное перечисление <abbr title="Enumeration">`Enum`</abbr> Python.

### Создание класса `Enum` { #create-an-enum-class }

Импортируйте `Enum` и создайте подкласс, который наследуется от `str` и `Enum`.

Мы наследуемся от `str`, чтобы документация API могла понять, что значения должны быть типа `string` и отображалась правильно.

Затем создайте атрибуты класса с фиксированными допустимыми значениями:

{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}

/// tip | Подсказка

Если интересно, то "AlexNet", "ResNet" и "LeNet" - это названия <abbr title="Технически, архитектуры моделей глубокого обучения">моделей</abbr> Машинного обучения.

///

### Определение *параметра пути* { #declare-a-path-parameter }

Определите *параметр пути*, используя в аннотации типа класс перечисления (`ModelName`), созданный ранее:

{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}

### Проверьте документацию { #check-the-docs }

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

<img src="/img/tutorial/path-params/image03.png">

### Работа с *перечислениями* в Python { #working-with-python-enumerations }

Значение *параметра пути* будет *элементом перечисления*.

#### Сравнение *элементов перечисления* { #compare-enumeration-members }

Вы можете сравнить это значение с *элементом перечисления* класса `ModelName`:

{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}

#### Получение *значения перечисления* { #get-the-enumeration-value }

Можно получить фактическое значение (в данном случае - `str`) с помощью `model_name.value` или в общем случае `your_enum_member.value`:

{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}

/// tip | Подсказка

Значение `"lenet"` также можно получить с помощью `ModelName.lenet.value`.

///

#### Возврат *элементов перечисления* { #return-enumeration-members }

Из *операции пути* можно вернуть *элементы перечисления*, даже вложенные в тело JSON (например в `dict`).

Они будут преобразованы в соответствующие значения (в данном случае - строки) перед их возвратом клиенту:

{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
Вы отправите клиенту такой JSON-ответ:

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

## Path-параметры, содержащие пути { #path-parameters-containing-paths }

Предположим, что есть *операция пути* с путем `/files/{file_path}`.

Но вам нужно, чтобы `file_path` сам содержал *путь*, например, `home/johndoe/myfile.txt`.

Тогда URL для этого файла будет такой: `/files/home/johndoe/myfile.txt`.

### Поддержка OpenAPI { #openapi-support }

OpenAPI не поддерживает способов объявления *параметра пути*, содержащего внутри *путь*, так как это может привести к сценариям, которые сложно определять и тестировать.

Тем не менее это можно сделать в **FastAPI**, используя один из внутренних инструментов Starlette.

Документация по-прежнему будет работать, хотя и не добавит никакой информации о том, что параметр должен содержать путь.

### Конвертер пути { #path-convertor }

Благодаря одной из опций Starlette, можете объявить *параметр пути*, содержащий *путь*, используя URL вроде:

```
/files/{file_path:path}
```

В этом случае `file_path` - это имя параметра, а часть `:path`, указывает, что параметр должен соответствовать любому *пути*.

Можете использовать так:

{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}

/// tip | Подсказка

Возможно, вам понадобится, чтобы параметр содержал `/home/johndoe/myfile.txt` с ведущим слэшем (`/`).

В этом случае URL будет таким: `/files//home/johndoe/myfile.txt`, с двойным слэшем (`//`) между `files` и `home`.

///

## Резюме { #recap }

Используя **FastAPI** вместе со стандартными объявлениями типов Python (короткими и интуитивно понятными), вы получаете:

* Поддержку редактора кода (проверку ошибок, автозавершение и т.п.)
* "<abbr title="преобразование строк из HTTP-запроса в типы данных Python">Парсинг</abbr>" данных
* Валидацию данных
* Аннотации API и автоматическую документацию

И объявлять типы достаточно один раз.

Это, вероятно, является главным заметным преимуществом **FastAPI** по сравнению с альтернативными фреймворками (кроме <abbr title="не считая оптимизаций">сырой</abbr> производительности).
