HTTP-методы: GET, POST и обработка форм
Один и тот же URL может обслуживать разные действия в зависимости от HTTP-метода: GET — показать, POST — отправить. Flask различает их параметром methods.
GET читает данные (открыть страницу, форму), POST изменяет состояние (отправить форму, создать запись). По умолчанию маршрут принимает только GET. Чтобы принимать POST, метод указывают явно.
HTTP-методы — это «глаголы» запроса. GET говорит «дай мне», POST — «вот данные, обработай». Это не формальность: GET должен быть безопасным (не менять данные) и идемпотентным, POST — наоборот, предназначен для изменений. Поисковики и браузеры на это полагаются.
from flask import request, redirect, url_for
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get("username", "")
# ... проверка ...
return redirect(url_for("dashboard"))
return "<form method='post'>...</form>"
Данные читают из объекта request: request.form — поля POST-формы, request.args — параметры строки запроса (?q=flask), request.values — оба вместе. Используй .get() вместо квадратных скобок — он вернёт None или дефолт, а не уронит запрос на отсутствующем ключе.
Различие между безопасными и небезопасными методами — не педантизм, а контракт всего веба. Браузеры свободно повторяют и предзагружают GET-запросы, поисковые роботы ходят по ним без спроса. Если повесить изменение данных на GET (скажем, /delete/5), однажды робот или префетч браузера незаметно удалит записи. Поэтому правило железное: читаешь — GET, меняешь — POST, и после успешного POST всегда редирект на GET. Этот паттерн POST/Redirect/GET ты будешь применять в каждой форме курса. Чтение полей через .get() с дефолтом — вторая привычка: пользовательский ввод непредсказуем, и приложение не должно падать на отсутствующем поле.
Как работает под капотом
Ключевой паттерн обработки форм — POST/Redirect/GET (PRG). После успешного POST не отдавай страницу напрямую, а делай redirect на GET-страницу. Иначе при обновлении (F5) браузер повторит POST — и форма отправится дважды.
Браузер Сервер
─────── ──────
POST /login (данные) ────────▶ обработать, сохранить
◀──────── 302 Redirect → /dashboard
GET /dashboard ────────▶ показать результат
◀──────── 200 страница
F5 повторяет GET (безопасно), а не POST
Без редиректа F5 повторил бы POST и продублировал данные. Смоделируем разбор формы и решение PRG обычным Python.
def handle_request(method, form):
if method == "POST":
username = form.get("username", "")
if not username:
return ("200", "Ошибка: пустое имя")
# сохранили — отдаём редирект, а не страницу
return ("302", "/dashboard")
return ("200", "Показать форму логина")
print(handle_request("GET", {}))
print(handle_request("POST", {"username": "anna"}))
print(handle_request("POST", {})) # пустая форма
Запусти: GET показывает форму, валидный POST отдаёт 302-редирект, пустой POST — сообщение об ошибке. Это и есть скелет любой обработки формы во Flask.
Частые ошибки
- Забыть methods=["POST"]. Тогда POST-запрос получит 405 Method Not Allowed.
- Обращаться request.form["key"] напрямую. Отсутствующий ключ кидает 400. Используй .get().
- Отдавать страницу после POST без редиректа. F5 продублирует отправку. Всегда PRG.
Best practices
- Меняющие данные действия — только через POST (и PRG-редирект после).
- Читай поля через .get() с дефолтом, не доверяй наличию ключей.
- Стройте URL для редиректа через url_for, а не хардкодом строки.
Что запомнить
- По умолчанию маршрут принимает только GET; POST разрешают через methods.
- GET — читать (безопасно), POST — менять состояние.
- Данные читают из request.form (POST) и request.args (URL), через .get().
- После успешного POST всегда делают редирект (паттерн PRG).
Итог: методы различают намерение запроса; methods разрешает POST на маршруте; данные читаются из request.form и request.args; а паттерн POST/Redirect/GET спасает от двойной отправки. Дальше разберём объект request целиком.