Каждый раз, когда пользователь открывает страницу в браузере или мобильное приложение отправляет запрос на сервер, под капотом запускается цепочка сетевых взаимодействий. Понимание протокола HTTP и клиент-серверной архитектуры — это фундамент, без которого невозможно осознанно проектировать бэкенд на Python и эффективно использовать фреймворки вроде Django.


1. Клиент-серверная модель, URL и DNS

Веб-приложения строятся по классической клиент-серверной архитектуре (Client–Server Architecture):

  • Клиент (Client) — сторона, инициирующая взаимодействие (веб-браузер, мобильное приложение, консольная утилита curl или другой микросервис). Клиент формирует запрос и ожидает ответ.
  • Сервер (Server) — удаленный компьютер или процесс, работающий непрерывно, слушающий входящие сетевые подключения, обрабатывающий бизнес-логику и возвращающий результат.
text
[ Клиент (Браузер / cURL) ]  ─── HTTP Request ───►  [ Сервер (Django / WSGI) ]
[                           ]  ◄─── HTTP Response ───  [                         ]

Как адрес превращается в соединение: DNS и URL

Когда в адресную строку вводится URL, например https://kurslar.prowid.com:443/courses/django/?ref=top#reviews:

  1. Схема протокола (https://): указывает правила передачи данных (зашифрованный HTTP over TLS).
  2. Доменное имя хоста (kurslar.prowid.com): понятное человеку символьное имя.
  3. DNS-разрешение (Domain Name System): браузер обращается к DNS-серверу, чтобы преобразовать домен в реальный IP-адрес сервера (например, 185.199.108.153).
  4. Порт (:443 для HTTPS, :80 для HTTP по умолчанию): числовой идентификатор сокета целевой программы на сервере.
  5. Путь к ресурсу (/courses/django/): путь, по которому веб-приложение определяет, какой именно ресурс запрошен.
  6. Query-параметры (?ref=top): строка запроса из пар ключ=значение, передающая дополнительные фильтры или метаданные.
  7. Якорь / фрагмент (#reviews): идентификатор для перехода к блоку внутри страницы (обрабатывается только браузером и на сервер вообще не передается).

2. Анатомия HTTP-запроса и HTTP-ответа

Протокол HTTP (HyperText Transfer Protocol) — это текстовый протокол прикладного уровня, работающий поверх надежного транспортного протокола TCP.

Структура HTTP-запроса (HTTP Request)

HTTP-запрос состоит из трех ключевых частей:

  1. Стартовая строка (Request Line): HTTP-метод, относительный путь и версия протокола.
  2. Заголовки (Headers): метаданные в формате Ключ: Значение (тип принимаемого контента, авторизация, куки, браузер клиента).
  3. Пустая строка-разделитель (\r\n).
  4. Тело запроса (Body / Payload): передается при отправке данных на сервер (JSON, данные формы, файлы).
http
POST /api/v1/articles/ HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0
Content-Type: application/json
Accept: application/json
Content-Length: 54

{"title": "Intro to Django", "published": true}

Основные HTTP-методы

МетодНазначениеИдемпотентностьНаличие тела
GETПолучение данных без изменения состояния сервераДаНет
POSTСоздание нового ресурса или выполнение действияНетДа
PUTПолная замена существующего ресурсаДаДа
PATCHЧастичное обновление полей ресурсаНетДа
DELETEУдаление ресурсаДаОбычно нет
HEADПолучение только заголовков без тела ответаДаНет
OPTIONSОпрос поддерживаемых сервером методов (CORS)ДаНет

📌 Идемпотентность означает, что многократный повторный вызов одного и того же запроса с одинаковыми параметрами приводит к тому же результату на сервере, что и однократный вызов.

Структура HTTP-ответа (HTTP Response)

HTTP-ответ сервера имеет симметричную структуру:

  1. Строка статуса (Status Line): версия протокола, цифровой код статуса и текстовое пояснение.
  2. Заголовки ответа (Response Headers): метаданные о типе и размере ответа, дате, куках и политиках кэширования.
  3. Пустая строка-разделитель.
  4. Тело ответа (Response Body): HTML-документ, JSON-объект, изображение или бинарный файл.
http
HTTP/1.1 200 OK
Date: Wed, 30 Sep 2026 07:00:00 GMT
Server: WSGIServer/0.2 CPython/3.13.1
Content-Type: text/html; charset=utf-8
Content-Length: 128

<!DOCTYPE html>
<html>
  <head><title>Django Course</title></head>
  <body><h1>Добро пожаловать в Django!</h1></body>
</html>

Классы кодов состояния HTTP

  • 1xx (Информационные): запрос принят, процесс продолжается (101 Switching Protocols для WebSocket).
  • 2xx (Успех):
  • 200 OK — стандартный успешный ответ.
  • 201 Created — ресурс успешно создан (в ответ на POST).
  • 204 No Content — действие выполнено успешно, тело ответа пустое (часто для DELETE).
  • 3xx (Перенаправление):
  • 301 Moved Permanently — постоянный редирект (кэшируется браузерами).
  • 302 Found (или 303 See Other / 307 Temporary Redirect) — временный редирект (Django по умолчанию использует 302 при вызове redirect()).
  • 4xx (Ошибки клиента):
  • 400 Bad Request — синтаксическая ошибка или невалидные данные формы.
  • 401 Unauthorized — требуется аутентификация.
  • 403 Forbidden — аутентификация пройдена, но прав недостаточно.
  • 404 Not Found — запрашиваемый URL не найден на сервере.
  • 405 Method Not Allowed — метод не поддерживается данным эндпоинтом (например, POST на чисто GET-страницу).
  • 422 Unprocessable Entity — синтаксис корректен, но нарушены бизнес-правила валидации (часто в FastAPI).
  • 5xx (Ошибки сервера):
  • 500 Internal Server Error — необработанное исключение в Python-коде.
  • 502 Bad Gateway — промежуточный Nginx не смог связаться с бэкенд-сервером Gunicorn/Uvicorn.
  • 503 Service Unavailable — сервер перегружен или находится на техобслуживании.
  • 504 Gateway Timeout — бэкенд превысил лимит времени на обработку запроса.

3. Наблюдаем «сырой» обмен через curl -v

Инструмент командной строки curl с флагом подробного вывода -v (verbose) позволяет увидеть реальный обмен байтами между клиентом (>) и сервером (<):

bash
curl -v -X POST "http://127.0.0.1:8000/api/feedback/" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"email": "student@example.com", "score": 5}'

Терминальный вывод наглядно демонстрирует соединение:

text
*   Trying 127.0.0.1:8000...
* Connected to 127.0.0.1 (127.0.0.1) port 8000
> POST /api/feedback/ HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/8.7.1
> Content-Type: application/json
> Accept: application/json
> Content-Length: 44
> 
* upload completely sent off: 44 bytes
< HTTP/1.1 201 Created
< Date: Wed, 30 Sep 2026 07:05:00 GMT
< Server: WSGIServer/0.2 CPython/3.13.1
< Content-Type: application/json
< Content-Length: 35
< 
{"status":"ok","id":42,"saved":true}
* Connection #0 to host 127.0.0.1 left intact

4. Что такое WSGI и почему нам нужен фреймворк

В мире Python взаимодействие между веб-сервером (например, Gunicorn, Nginx Unit или встроенным сервером разработки) и кодом приложения стандартизировано спецификацией PEP 3333 — WSGI (Web Server Gateway Interface).

Простейшее низкоуровневое WSGI-приложение без фреймворков — это обычная вызываемая функция:

python
# simple_wsgi_app.py
from urllib.parse import parse_qs

def application(environ, start_response):
    """
    Низкоуровневая точка входа WSGI.
    environ: словарь с заголовками, методом и переменными окружения запроса.
    start_response: колбэк для отправки строки статуса и заголовков.
    """
    method = environ.get('REQUEST_METHOD', 'GET')
    path = environ.get('PATH_INFO', '/')
    query_string = environ.get('QUERY_STRING', '')
    query_params = parse_qs(query_string)

    if path == '/':
        status = '200 OK'
        headers = [('Content-Type', 'text/html; charset=utf-8')]
        body = b"<h1>Main Page</h1><p>Running raw WSGI</p>"
    elif path == '/hello' and method == 'GET':
        name = query_params.get('name', ['Guest'])[0]
        status = '200 OK'
        headers = [('Content-Type', 'text/plain; charset=utf-8')]
        body = f"Hello, {name}!".encode('utf-8')
    else:
        status = '404 Not Found'
        headers = [('Content-Type', 'text/plain; charset=utf-8')]
        body = b"404 Page Not Found"

    start_response(status, headers)
    return [body]

Запустить такое приложение можно стандартным модулем Python:

python
# run_server.py
from wsgiref.simple_server import make_server
from simple_wsgi_app import application

if __name__ == '__main__':
    server = make_server('127.0.0.1', 8000, application)
    print("Serving raw WSGI on http://127.0.0.1:8000 ...")
    server.serve_forever()

Посмотрев на этот код, легко понять, какой огромный объем рутины берет на себя веб-фреймворк:

  1. Маршрутизация (Routing): разбор регулярных выражений, конвертация параметров типов (<int:id>, <slug:slug>).
  2. Безопасность: защита от CSRF-атак, XSS, SQL-инъекций, Clickjacking, безопасное управление сессиями.
  3. Парсинг форм и файлов: обработка multipart/form-data, валидация данных, загрузка медиафайлов на диск.
  4. Работа с базой данных: ORM, пул соединений, транзакции, миграции схемы.
  5. Шаблонизация: безопасный рендеринг динамического HTML с наследованием, фильтрами и экранированием.
  6. Аутентификация: пользователи, хэширование паролей, группы, права доступа, куки и сессии.

5. Обзор современных веб-фреймворков на Python

  • Django («Batteries Included»): монолитный полнофункциональный фреймворк. Содержит из коробки всё: ORM, миграции, готовую панель администратора, формы, авторизацию, защиту от типовых уязвимостей. Идеален для контентных сайтов, e-commerce, ERP/CRM-систем и крупных порталов.
  • FastAPI: современный асинхронный (ASGI) микрофреймворк, построенный на аннотациях типов Python, валидации Pydantic и стандарте OpenAPI. Фокусируется на создании молниеносных REST/JSON API и микросервисов.
  • Flask: минималистичный микрофреймворк («micro-framework»), предоставляющий только ядро маршрутизации и шаблонизатор Jinja2, оставляя выбор ORM и авторизации разработчику.

6. Типичные заблуждения новичков

⚠️ Заблуждение 1: «Сервер помнит состояние клиента между запросами автоматически».
Протокол HTTP является stateless (не сохраняет состояние). Каждый отдельный запрос изолирован. Чтобы сервер узнал клиента при следующем переходе, используются специальные механизмы: заголовки Cookie и Set-Cookie, сессии на стороне сервера или токены авторизации (JWT / Bearer).

⚠️ Заблуждение 2: «Разница между GET и POST только в том, что в POST параметры скрыты».
GET предназначен исключительно для безопасного чтения данных без побочных эффектов (safe & idempotent). Параметры GET кэшируются браузером и прокси-серверами, сохраняются в истории браузера и логах веб-серверов. POST предназначен для мутаций (создание заказа, смена пароля, отправка формы) и никогда не должен заменяться на GET.


7. Резюме

  • Архитектура веба основана на обмене сообщениями HTTP Request ↔ HTTP Response между клиентом и сервером.
  • HTTP-запрос состоит из стартовой строки с методом, заголовков и опционального тела.
  • Ответ содержит код состояния (2xx, 3xx, 4xx, 5xx), заголовки и тело ответа.
  • Спецификация WSGI связывает веб-сервер с Python-кодом через функцию application(environ, start_response).
  • Веб-фреймворк Django автоматизирует всю инфраструктурную рутину (роутинг, ORM, безопасность, сессии, шаблоны).
  • В следующем уроке мы разберем внутреннюю архитектуру Django и архитектурный паттерн MTV (Model-Template-View).