# Параметры пути, параметры запроса и тело<no value>

## Что мы изучим?

Клиенту нужно сказать серверу, **что** именно он хочет. Для этого есть три способа:

| Способ | Где | Пример |
|---|---|---|
| **Параметр пути** (Path Parameter) | Часть самого пути | `/students/7` |
| **Параметры запроса** (Query Parameters) | После `?` в URL | `/students?city=Haifa&limit=10` |
| **Тело** (Body) | В теле запроса (JSON) | `{"name": "Dana", "age": 24}` |

## Зачем это нужно?

В следующем уроке мы напишем первый сервер, и в FastAPI каждый из трёх способов определяется по-своему. Если не понимать разницу, информация попадает не туда: идентификатор — в тело, а пароль — в URL. А каждый настоящий API, которым вы будете пользоваться (GitHub, погода, платежи), использует все три.

## Объяснение с основ

### Параметр пути — «какой именно?»

**Переменная** часть внутри пути, которая идентифицирует **конкретный ресурс**:

```text
GET /students/7
              └─ path parameter: which student
GET /users/octocat/repos
           └─ path parameter: which user
```

**Когда:** когда вы идентифицируете **один элемент** (обычно по id). Без параметра путь неполный.

### Параметры запроса — «как? сколько? отфильтровать по чему?»

Пары `key=value` после `?`, разделённые `&`:

```text
GET /students?city=Haifa&min_age=20&limit=10
             └──────────── query string ────────┘
                key=value & key=value & key=value
```

**Когда:** **фильтрация**, **сортировка**, **постраничный вывод** (Pagination) и **опции**. Обычно **необязательны**: `GET /students` работает и без них и просто возвращает всё.

### Тело — «вот данные»

Сами данные, в теле запроса, почти всегда в **JSON**:

```text
POST /students
Content-Type: application/json

{"name": "Dana", "age": 24, "email": "dana@mail.com"}
```

**Когда:** когда вы **отправляете данные** для создания или обновления (POST, PUT). Можно отправлять сложные структуры (списки, вложенные объекты) и большие объёмы. **У GET и DELETE тела нет.**

### Как решить?

| Вопрос | Способ |
|---|---|
| Идентифицирует, **какой** элемент? | **Путь** — `/students/7` |
| Фильтрует, сортирует, ограничивает количество, опция отображения? | **Строка запроса** — `?city=Haifa&sort=age` |
| **Данные** ресурса, который вы создаёте или обновляете? | **Тело** — JSON |
| Чувствительная информация (пароль, токен)? | **Никогда в URL** (он сохраняется в логах и в истории браузера). Тело или заголовки (Headers) |

{{< box "analogy" "Аналогия: заказ пиццы по телефону" >}}
«Я звоню в **хайфский филиал**» (**путь**: какое именно место). «Расскажите об акциях, **только веганские**, **сначала самые дешёвые**» (**строка запроса**: фильтрация и сортировка того, что я спрашиваю). «Хочу заказать: **семейную пиццу, маслины, грибы, на улицу Герцля, 10**» (**тело**: полные данные того, что создаётся).
{{< /box >}}

### Кодирование URL (URL Encoding)

В URL пробелы и специальные символы нельзя писать «как есть». Пробел становится `%20` (или `+`), `&` внутри значения становится `%26`, а неанглийские буквы (например, на иврите или русском) кодируются в байты (`%D7%93...`). Кодирование за нас делают браузер и библиотеки — но важно понимать, зачем оно существует.

## Разбор адреса

```text
/students/7/grades?semester=2&limit=5

path   ->  students / 7 / grades   (the grades of student 7)
query  ->  semester = 2            (a filter)
           limit    = 5            (how many)
```

- **Путь** идентифицирует, **какой** ресурс: оценки студента 7.
- **Строка запроса** начинается после `?`, и параметры разделяются `&`. Она говорит, **как**: какой семестр и сколько результатов.

## Все три в одном запросе

```text
PUT /students/7?notify=true
Content-Type: application/json

{"name": "Dana Cohen", "age": 25}
```

| Часть | Значение | Почему здесь |
|---|---|---|
| Путь | `7` | **Какой** студент — часть идентичности ресурса |
| Строка запроса | `notify=true` | **Опция** запроса — не часть данных |
| Тело | `{"name": ..., "age": ...}` | **Сами новые данные** |
| Заголовок | `Content-Type: application/json` | **В каком формате** тело |

В следующем уроке мы увидим, как FastAPI автоматически принимает все три — и кладёт каждый в параметр нашей функции.

## Типичная ошибка

**Ошибка 1: идентификатор в строке запроса вместо пути — или наоборот.**

```text
GET /students?id=7          ← works, but not the convention for ONE resource
GET /students/city/Haifa    ← "Haifa" is a filter, not a resource id
```

Соглашение: **конкретный элемент — в пути** (`/students/7`), **фильтрация — в строке запроса** (`/students?city=Haifa`).

**Ошибка 2: значение с пробелами и `&` «как есть» в адресе.**

```text
/search?q=rock & roll
```

Пробелы ломают адрес, а `&` воспринимается как начало нового параметра. Значение нужно **закодировать**: `q=rock+%26+roll`.

**Ошибка 3: забыть, что всё в URL — строка.** `"7"` ≠ `7`. Сравнение `if student_id == 7`, когда `student_id` пришёл из URL как `"7"`, → `False`. (FastAPI преобразует за нас, если мы укажем тип.)

**Ошибка 4: чувствительная информация в URL.** `GET /login?user=dana&password=1234`. URL сохраняется в истории браузера, в логах сервера и в ссылках, которые вы отправляете. Пароли — **только в теле** (и всегда через HTTPS).

<!-- exercises:start — generated by scripts/intro6/sync_site_exercises.py; edit scripts/intro6/exercises/ -->
## Практика

Упражнения опираются только на то, что пройдено в этом уроке и в предыдущих. Сначала попробуйте сами, и только потом открывайте решение.

### Упражнение 1: Куда идёт каждый кусок данных?

Для каждого запроса — являются ли данные параметром пути, параметром запроса или телом?

1. Идентификатор студента, которого вы хотите получить.
2. Фильтрация студентов по городу.
3. Имя и возраст нового студента.
4. Сколько результатов возвращать на страницу.
5. Обновлённые данные существующего студента.

{{< solution >}}
1. **Путь** (Path) — идентифицирует, **какой** ресурс: `/students/7`.
2. **Строка запроса** (Query) — фильтрация: `/students?city=Haifa`.
3. **Тело** (Body) — данные нового ресурса в JSON.
4. **Строка запроса** — опция: `?limit=10`.
5. **Тело** (а идентификатор — в **пути**): `PUT /students/7` с телом.

Правило: **путь** — какой ресурс. **Строка запроса** — фильтрация, сортировка и страницы. **Тело** — сами данные.
{{< /solution >}}

### Упражнение 2: Разбор адреса

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

```text
https://api.kodkod.dev/students/7/grades?semester=2&limit=5
```

{{< solution >}}
| Часть | Значение | Смысл |
|---|---|---|
| Путь | `/students/7/grades` | Оценки студента 7 |
| Идентификатор в пути | `7` | **Какой** студент |
| Строка запроса | `semester=2` | Фильтр — семестр 2 |
| Строка запроса | `limit=5` | Сколько результатов — 5 |

Строка запроса начинается после `?`, а параметры разделяются `&`.
{{< /solution >}}

### Упражнение 3: Строим адрес

Напишите адреса для следующих запросов к серверу `https://api.x.com`:

1. Все студенты из Хайфы.
2. Студенты из Хайфы, первые десять.
3. Студент 7.
4. Оценки студента 7 в семестре 1.

{{< solution >}}
```text
1. https://api.x.com/students?city=Haifa
2. https://api.x.com/students?city=Haifa&limit=10
3. https://api.x.com/students/7
4. https://api.x.com/students/7/grades?semester=1
```

Идентификатор идёт **в путь**, а фильтры — **в строку запроса**, после `?` и через `&`.
{{< /solution >}}

### Упражнение 4: Все три в одном запросе

```text
PUT /students/7?notify=true
Content-Type: application/json

{"name": "Dana Cohen", "age": 25}
```

Определите параметр пути, параметр запроса и тело и объясните, почему каждый кусок данных находится на своём месте. Какова роль заголовка `Content-Type`?

{{< solution >}}
- **Путь:** `7` — **какой** студент обновляется. Это часть идентичности ресурса.
- **Строка запроса:** `notify=true` — **опция** запроса: как его выполнить. Это не часть данных студента.
- **Тело:** `{"name": "Dana Cohen", "age": 25}` — **сами новые данные**.
- **`Content-Type: application/json`** — сообщает серверу, **в каком формате** тело, чтобы он знал, как его читать.
{{< /solution >}}

### Упражнение 5: Что не идёт в адрес?

Для каждого адреса — в чём проблема и как сделать правильно?

```text
1. GET /students?id=7
2. GET /students/city/Haifa
3. GET /login?user=dana&password=1234
4. GET /search?q=rock & roll
```

{{< solution >}}
1. **Идентификатор одного элемента принадлежит пути**: `GET /students/7`. Строка запроса предназначена для фильтрации.
2. **«Haifa» — фильтр, а не идентификатор ресурса**: `GET /students?city=Haifa`.
3. **Пароль в адресе** сохраняется в истории браузера, в логах сервера и в ссылках, которые вы отправляете. Пароли — **только в теле**, и всегда через HTTPS.
4. **Пробелы и `&` внутри значения** ломают адрес: `&` воспринимается как начало нового параметра. Значение нужно **закодировать** (URL Encoding): `q=rock+%26+roll`. На практике кодирование за нас делают браузер и библиотеки.
{{< /solution >}}

<!-- exercises:end -->

## Итоги

- **Параметр пути** (`/students/7`) — **какой** ресурс. Часть пути.
- **Параметры запроса** (`?city=Haifa&limit=10`) — **фильтрация, сортировка, постраничный вывод, опции**. Обычно необязательны.
- **Тело** (JSON) — **данные** для создания и обновления (POST, PUT). У GET и DELETE тела нет.
- Всё в URL — **строка**. Строку запроса нужно правильно кодировать (пробелы, `&` и неанглийские буквы).
- Не кладите пароли и чувствительную информацию в URL.
- В следующих уроках FastAPI будет автоматически разбирать все три, включая преобразование типов.

<!-- docs:start — generated by scripts/intro6/site_docs.py; edit the links there -->

## Официальная документация

- [MDN — What is a URL?](https://developer.mozilla.org/en-US/docs/Learn_web_development/Howto/Web_mechanics/What_is_a_URL)
- [MDN — HTTP messages: Body](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Messages#request_body)

<!-- docs:end -->
