Ответы, редиректы, url_for и коды состояния

View не обязана возвращать только строку. Она управляет статус-кодом, заголовками и редиректами — то есть всем HTTP-ответом целиком.
Ответ — это статус (200, 404, 302), заголовки и тело. Flask даёт удобные инструменты: redirect для перенаправлений, url_for для построения ссылок по имени endpoint, make_response для тонкой настройки.

Статус-код — это короткий «диагноз» ответа. 2xx — успех, 3xx — перенаправление, 4xx — ошибка клиента, 5xx — ошибка сервера. Возвращать правильный код важно: на 404 поисковик уберёт страницу из индекса, на 302 браузер пойдёт по новому адресу.

from flask import redirect, url_for, make_response, abort

@app.route("/old")
def old():
    return redirect(url_for("new"))     # 302 на endpoint new

@app.route("/new")
def new():
    return "Новая страница"

@app.route("/secret")
def secret():
    abort(403)                          # прервать с кодом 403

Ключевой инструмент — url_for. Он строит URL по имени view-функции, а не по строке. Поменяешь путь в маршруте — все ссылки через url_for обновятся сами. Хардкод строк "/new" — путь к битым ссылкам после рефакторинга.

url_for("show_user", user_id=42)   # → "/user/42"
url_for("static", filename="app.css")  # → "/static/app.css"

url_for стоит сделать рефлексом: каждый раз, когда хочется написать строку URL руками, останавливайся и зови url_for. Выгода видна не сразу, а в момент рефакторинга — когда ты меняешь путь маршрута, и все ссылки в коде и шаблонах обновляются сами, без поиска по проекту и битых страниц. То же касается статики: url_for('static', filename=...) переживёт смену настроек статической папки или подключение CDN. Правильные статус-коды — отдельная дисциплина: 404 убирает несуществующую страницу из индекса, 301 против 302 определяет, закэширует ли браузер переезд, а 4xx против 5xx подсказывает клиенту, чья это ошибка. Возвращать осмысленный код так же важно, как и тело ответа.

Как работает под капотом

url_for делает обратную операцию к маршрутизации: из имени endpoint и параметров собирает путь, подставляя переменные части. Маршрутизация шла «путь → функция», а url_for — «функция → путь». Поэтому ссылки переживают изменение URL.

  Маршрут:   "/user/<int:user_id>"  ──эндпоинт──▶ show_user
                                                      ▲
  url_for("show_user", user_id=42)  ──обратно──┘
                                       подставить 42
                                       ▼
                                    "/user/42"

Смоделируем url_for: по имени endpoint и шаблону подставим параметры в путь.

import re

url_map = {
    "show_user": "/user/<user_id>",
    "show_post": "/post/<slug>",
    "index": "/",
}

def url_for(endpoint, **params):
    template = url_map[endpoint]
    def repl(m):
        name = m.group(1)
        return str(params[name])
    return re.sub(r"<(\w+)>", repl, template)

print(url_for("show_user", user_id=42))
print(url_for("show_post", slug="hello-flask"))
print(url_for("index"))

Запусти: имя endpoint + параметры превращаются в готовый путь. Поменяй шаблон в url_map — и все ссылки обновятся, не трогая код вызова. Ровно ради этого Flask настаивает на url_for вместо строк.

Частые ошибки

  • Хардкодить URL строками. После смены маршрута ссылки ломаются. Всегда url_for.
  • Возвращать redirect без return. redirect только создаёт ответ — его надо вернуть из функции.
  • Путать 301 и 302. 301 — навсегда (кэшируется браузером), 302 — временно. Для постоянных переездов бери 301 осознанно.

Best practices

  • Все внутренние ссылки — через url_for, в том числе в шаблонах.
  • Используй abort(code) для ранних выходов с ошибкой вместо ручной сборки ответа.
  • Для кастомных заголовков/кук бери make_response и настраивай объект Response.

Что запомнить

  • View управляет всем ответом: телом, статусом, заголовками.
  • redirect перенаправляет, abort прерывает запрос с нужным кодом.
  • url_for строит устойчивые ссылки по имени endpoint, переживая смену URL.
  • Корректный статус-код важен для браузеров и поисковиков.

Итог: view управляет всем ответом — статусом, заголовками, телом. redirect перенаправляет, url_for строит устойчивые ссылки по имени endpoint, abort прерывает с нужным кодом. Это завершает блок про маршруты и запросы — дальше шаблоны.

Проверьте себя
1. Почему ссылки строят через url_for, а не строкой?
Aurl_for быстрее
BПри изменении маршрута ссылки обновляются автоматически
CСтроки запрещены в Flask
Durl_for шифрует URL
2. Что означает статус-код 302?
AСтраница не найдена
BВременное перенаправление
CОшибка сервера
DУспех с пустым телом