Skip to content

Latest commit

 

History

History
327 lines (227 loc) · 16.6 KB

File metadata and controls

327 lines (227 loc) · 16.6 KB

Урок 15. HTTP и REST

Что происходит когда вы открываете сайт

Прежде чем писать бэкенд, нужно понять как вообще работает веб. Когда вы вводите адрес в браузере и нажимаете Enter — происходит не магия, а чёткая последовательность действий которую можно объяснить и воспроизвести в коде.

Браузер отправляет запрос на сервер. Сервер обрабатывает запрос и отправляет ответ. Браузер отображает результат.

Протокол по которому происходит этот обмен называется HTTP (HyperText Transfer Protocol). Это текстовый протокол — запросы и ответы это обычный структурированный текст. Всё что делает браузер, мобильное приложение или ваш Python-скрипт обращаясь к серверу — отправляет HTTP-запросы и читает HTTP-ответы.


HTTP-запрос

HTTP-запрос состоит из трёх частей: стартовая строка, заголовки и тело.

GET /products?category=1 HTTP/1.1
Host: shop.example.com
Accept: application/json
Authorization: Bearer abc123

(тело пустое — GET-запросы обычно не имеют тела)

Стартовая строка содержит три элемента:

  • Метод — что мы хотим сделать (GET, POST, PUT, DELETE)
  • Путь — к какому ресурсу обращаемся (/products)
  • Версия протоколаHTTP/1.1

Заголовки — метаданные запроса: кто отправляет, какой формат принимает, токен авторизации и так далее.

Тело — данные которые отправляются серверу. Есть только у POST и PUT запросов — когда нужно передать новые данные.


HTTP-ответ

Ответ сервера имеет схожую структуру:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 234

{
    "id": 1,
    "name": "Ноутбук Lenovo",
    "price": 75000.00
}

Статусная строка содержит версию протокола и код статуса — трёхзначное число которое сразу говорит что произошло.

Заголовки — метаданные ответа: тип содержимого, размер, кэширование.

Тело — сами данные. В современных API это почти всегда JSON.


Коды статусов

Код статуса — главный способ которым сервер сообщает результат операции. Коды разбиты на группы по первой цифре:

2xx — Успех

Код Название Когда используется
200 OK Всё хорошо Успешный GET, успешный PUT/DELETE
201 Created Создано Успешный POST — создан новый ресурс
204 No Content Нет содержимого Успешное удаление — возвращать нечего

4xx — Ошибка клиента

Клиент сделал что-то не то: неверный запрос, нет прав, ресурс не найден.

Код Название Когда используется
400 Bad Request Неверный запрос Невалидные данные, неверный формат
401 Unauthorized Не авторизован Нужен токен авторизации
403 Forbidden Запрещено Авторизован, но нет прав
404 Not Found Не найдено Ресурс не существует
422 Unprocessable Entity Ошибка валидации Данные пришли, но не прошли проверку

5xx — Ошибка сервера

Клиент всё сделал правильно, но сервер не смог обработать запрос.

Код Название Когда используется
500 Internal Server Error Внутренняя ошибка Что-то сломалось на сервере
503 Service Unavailable Сервис недоступен Сервер перегружен или на обслуживании

Коды статусов — это соглашение, не обязательство. Технически можно вернуть 200 при ошибке. Но хороший API использует коды правильно — клиент сразу понимает что произошло без разбора тела ответа.


HTTP-методы

Метод говорит серверу что клиент хочет сделать с ресурсом. Четыре основных метода:

GET — получить данные

Запрашивает данные. Не изменяет состояние сервера. Может вызываться сколько угодно раз — результат одинаков:

GET /users          — получить список всех пользователей
GET /users/5        — получить пользователя с id=5
GET /products?category=1  — получить товары категории 1

POST — создать новый ресурс

Отправляет данные для создания новой записи. Каждый вызов создаёт новый ресурс:

POST /users
Body: {"name": "Алексей", "email": "alex@mail.ru"}

→ Создаёт нового пользователя
→ Возвращает 201 Created + данные созданного пользователя

PUT — обновить существующий ресурс

Полностью заменяет данные ресурса. Адресуется к конкретному объекту по id:

PUT /users/5
Body: {"name": "Алексей Смирнов", "email": "alex@mail.ru", "city": "Москва"}

→ Полностью обновляет пользователя с id=5
→ Возвращает 200 OK + обновлённые данные

DELETE — удалить ресурс

Удаляет указанный ресурс:

DELETE /users/5

→ Удаляет пользователя с id=5
→ Возвращает 204 No Content (тело пустое — удалять нечего)

Методы и CRUD

Вы уже работали с CRUD в SQL. HTTP-методы — это CRUD на уровне веба:

CRUD SQL HTTP-метод
Create INSERT POST
Read SELECT GET
Update UPDATE PUT
Delete DELETE DELETE

REST — соглашение об архитектуре

REST (Representational State Transfer) — это не протокол и не стандарт. Это архитектурный стиль — набор соглашений о том как проектировать API поверх HTTP.

Когда говорят "REST API" или "RESTful API" — имеют в виду что API следует этим соглашениям. Если оно не следует — это просто "HTTP API".

Главная идея REST: ресурсы и действия

В REST API URL описывает ресурс (существительное), а HTTP-метод описывает действие (глагол):

# Правильно: URL — существительное, действие — в методе
GET    /users        — получить всех пользователей
POST   /users        — создать пользователя
GET    /users/5      — получить пользователя 5
PUT    /users/5      — обновить пользователя 5
DELETE /users/5      — удалить пользователя 5

# Неправильно: глагол в URL — не REST
GET /getUsers
POST /createUser
GET /deleteUser?id=5

REST — набор архитектурных принципов и рекомендаций, а не технический стандарт. Нет программы которая проверит соответствует ли API стандарту REST. Разработчики следуют соглашениям добровольно: URL описывает ресурс, метод описывает действие, используются стандартные коды статусов. Это делает API предсказуемым и понятным.

Иерархия ресурсов

Связанные ресурсы выражаются через вложенные пути:

GET /users/5/orders          — все заказы пользователя 5
GET /users/5/orders/12       — конкретный заказ 12 пользователя 5
POST /users/5/orders         — создать заказ для пользователя 5

Основные принципы REST

Клиент-сервер — разделение ответственности. Фронтенд (браузер, мобильное приложение) и бэкенд (сервер) — это независимые части которые общаются через API. Фронтенд не знает как устроена база данных, бэкенд не знает как выглядит интерфейс.

Без состояния (stateless) — каждый запрос содержит всю информацию необходимую для его обработки. Сервер не помнит предыдущие запросы. Если нужна авторизация — токен передаётся в каждом запросе, а не хранится на сервере между обращениями.

Единообразный интерфейс — стандартные URL, стандартные методы, стандартные коды статусов. Разработчик знакомый с REST сразу понимает как работать с любым RESTful API.


JSON — формат обмена данными

JSON (JavaScript Object Notation) — текстовый формат для структурированных данных. Стал стандартом для REST API потому что:

  • Читается человеком
  • Парсится любым языком программирования
  • Напрямую отображается на Python-словари и списки
{
    "id": 1,
    "name": "Алексей Смирнов",
    "email": "alexey@gmail.com",
    "city": "Москва",
    "orders": [
        {
            "id": 10,
            "status": "delivered",
            "total": 80500.00
        }
    ]
}

Python умеет работать с JSON через встроенный модуль json:

import json

# Python → JSON (сериализация)
data = {'name': 'Алексей', 'city': 'Москва'}
json_string = json.dumps(data, ensure_ascii=False)
print(json_string)
# {"name": "Алексей", "city": "Москва"}

# JSON → Python (десериализация)
json_string = '{"id": 1, "name": "Алексей"}'
data = json.loads(json_string)
print(data['name'])   # Алексей

В FastAPI который мы изучим в следующих уроках — JSON-сериализация происходит автоматически. Вы возвращаете Python-словарь, FastAPI превращает его в JSON-ответ.


Как бэкенд соотносится с тем что вы уже знаете

К этому моменту вы умеете работать с базой данных через репозитории. Теперь посмотрим как это встраивается в бэкенд:

Клиент (браузер / мобильное приложение)
        │
        │  HTTP-запрос: GET /products/1
        ▼
┌──────────────────────────────────────┐
│           Бэкенд-приложение          │
│                                      │
│  Роутер       — разбирает URL и      │
│                 направляет к нужной  │
│                 функции-обработчику  │
│                                      │
│  Схема/       — проверяет данные     │
│  Валидация      запроса              │
│                                      │
│  Обработчик   — бизнес-логика        │
│  (view)                              │
│                                      │
│  Репозиторий  — работа с базой       │  ← это вы уже умеете
│                 данных               │
│                                      │
│  Схема        — формирует ответ      │
│  ответа         (сериализация)       │
└──────────────────────────────────────┘
        │
        │  HTTP-ответ: 200 OK + JSON
        ▼
Клиент получает данные и отображает их

Репозитории которые вы написали в Модуле 2 — это нижний слой этой схемы. В следующих уроках вы добавите остальные слои: роутеры, схемы, обработчики. Цепочка замкнётся.


Вопросы

  1. Из каких трёх частей состоит HTTP-запрос?
  2. Чем 400 отличается от 500?
  3. Какой HTTP-метод использовать чтобы получить список всех товаров? А чтобы создать новый товар?
  4. Что означает "REST — это не протокол, а соглашение"?
  5. Почему URL /getUsers нарушает принципы REST, а /users — нет?
  6. Что означает принцип "без состояния" (stateless) в REST?
  7. Какой код статуса вернуть если пользователь запросил товар с id=999, которого нет в базе?
  8. Чем PUT /users/5 отличается от POST /users?
  9. Зачем нужен JSON если Python умеет работать со словарями напрямую?
  10. Посмотрите на схему из урока. Какую часть этой схемы вы уже умеете реализовывать после Модуля 2?

Практика

Это концептуальный урок — вместо задач с кодом проверьте понимание через следующие вопросы. Ответьте письменно своими словами.


Вопрос 1.

Вы проектируете API для библиотеки из практической работы Модуля 1 (книги, авторы, читатели, выдачи). Опишите минимум 8 эндпоинтов: для каждого укажите метод, URL и что он делает.


Вопрос 2.

Клиент отправил POST /users с телом {"name": ""} (пустое имя). Какой код статуса должен вернуть сервер и почему?


Вопрос 3.

Объясните своими словами разницу между фронтендом и бэкендом используя принцип клиент-сервер из REST.


Предыдущий урок | Следующий урок