Ответ сервера

Flask предлагает три способа для создания ответа:

  1. Возврат строки напрямую или с использованием шаблонизатора
  2. Создание с помощью объекта ответа
  3. Формирование кортежа в формате (response, status, headers) или (response, headers)

Рассмотрим эти методы более подробно.

Создание ответа в виде строки

@app.route('/books/<genre>')
def books(genre):
    return "All Books in {} category".format(genre)

Этот подход предполагает отправку простой строки в качестве ответа клиенту. Если Flask получает строку от метода, он автоматически преобразует её в объект ответа через make_response(). Статус HTTP по умолчанию установлен на 200, а тип содержимого (content-type) назначается как text/html. Это обычно покрывает все базовые нужды. Тем не менее, для добавления дополнительных заголовков, рациональнее применить функцию make_response().

Создание ответа с помощью make_response()

Функция make_response() имеет следующий вид:

res_obj = make_response(res_body, status_code=200)

Первым требуемым параметром является res_body, обозначающее тело ответа. status_code же является необязательным и равен 200 по умолчанию.

Ниже продемонстрировано, как можно включить заголовки в ответ с помощью make_response().

from flask import Flask, make_response,

@app.route('/books/<genre>')
def books(genre):
    res = make_response("All Books in {} category".format(genre))
    res.headers['Content-Type'] = 'text/plain'
    res.headers['Server'] = 'Foobar'
    return res

Следующий пример иллюстрирует способ использования make_response() для ответов с ошибкой 404.

@app.route('/')
def http_404_handler():
    return make_response("<h2>404 Error</h2>", 400)

Настройка файлов cookie является важной задачей в веб-программировании, и make_response() позволяет сделать это легко. Приведенный ниже пример демонстрирует установку двух cookie в браузере пользователя.

@app.route('/set-cookie')
def set_cookie():
    res = make_response("Cookie setter")
    res.set_cookie("favorite-color", "skyblue")
    res.set_cookie("favorite-font", "sans-serif")
    return res

Примечание: куки обсуждаются подробно в разделе «Куки во Flask».

Установленные cookie будут действовать весь срок сессии браузера. Чтобы обозначить срок их действия в секундах, используйте третий параметр метода set_cookie(). Например:

@app.route('/set-cookie')
def set_cookie():
    res = make_response("Cookie setter")
    res.set_cookie("favorite-color", "skyblue", 60*60*24*15)
    res.set_cookie("favorite-font", "sans-serif", 60*60*24*15)
    return res

В данном примере, cookie устанавливаются сроком на 15 дней.

Создание ответов с помощью кортежей

Также возможно использовать кортежи для формирования ответа в одном из следующих форматов:

(response, status, headers)

(response, headers)

(response, status)

response — это строковое представление тела ответа, status обозначает HTTP-код состояния в числовом или строковом формате, а headers — словарь заголовков с их значениями.

@app.route('/')
def http_500_handler():
    return ("<h2>500 Error</h2>", 500)

Функция возвратит ошибку HTTP 500 Internal Server Error. Отметим, что, используя кортежи, мы можем опустить скобки вокруг элементов:

@app.route('/')
def http_500_handler():
    return "<h2>500 Error</h2>", 500

Пример ниже поясняет, как задавать заголовки с использованием кортежей:

@app.route('/')
def render_markdown():
    return "## Heading", 200, {'Content-Type': 'text/markdown'}

Как вы думаете, что делает следующая функция?

@app.route('/transfer')
def transfer():
    return "", 302, {'location': 'http://localhost:5000/login'}

Этот обработчик перенаправляет пользователя на URL http://localhost:5000/login с кодом состояния 302, что означает временное перенаправление. Так как перенаправления часто есть частью работы приложения, Flask позволяет упростить это с помощью функции redirect().

from flask import Flask, redirect

@app.route('/transfer')
def transfer():
    return redirect("http://localhost:5000/login")

По умолчанию redirect() создает 302-перенаправление. Если необходимо задать 301 статус, укажите это в параметрах функции.

from flask import Flask, redirect

@app.route('/transfer')
def transfer():
    return redirect("http://localhost:5000/login", code=301)

Перехват запросов

Для веб-приложений часто бывает нужно выполнить определенные действия до или после обработки запроса. Например, вы можете хотеть записывать IP-адреса всех пользователей или обеспечивать аутентификацию перед отображением приватных страниц. Вместо необходимости повторять один и тот же код в каждом обработчике представлений, Flask предоставляет декораторы для этих целей:

  • before_first_request: запускает функцию перед первым запросом.
  • before_request: выполняет функцию непосредственно перед обработкой каждого запроса.
  • after_request: выполняет функцию после успешного завершения запроса. Обратите внимание, что она не будет вызвана, если в процессе обработки запроса возникло исключение. Эта функция принимает объект ответа и должна вернуть тот же или измененный ответ.
  • teardown_request: действует аналогично after_request, но выполняется даже при исключениях во время обработки запроса.

Если функция, декорированная before_request, вернет ответ, обработчик конкретного запроса не будет вызван.

Ниже приведен пример использования этих декораторов во Flask. Создайте новый файл hooks.py и поместите в него следующий код:

from flask import Flask, request, g

app = Flask(__name__)

@app.before_first_request
def before_first_request():
    print("before_first_request() called")

@app.before_request
def before_request():
    print("before_request() called")

@app.after_request
def after_request(response):
    print("after_request() called")
    return response

@app.route("/")
def index():
    print("index() called")
    return '<p>Testing Request Hooks</p>'

if __name__ == "__main__":
    app.run(debug=True)

Запустите сервер и откройте в браузере страницу http://localhost:5000/. В консоли, где происходит выполнение сервера, должны появиться следующие записи:

before_first_request() called
before_request() called
index() called
after_request() called

Примечание: логи обработки запросов сервером удалены для удобства восприятия материала.

При обновлении страницы в консоли появится следующее:

before_request() called
index() called
after_request() called

Поскольку запрос повторный, вызова функции before_first_request() больше не происходит.

Отмена запроса с помощью abort()

Flask предлагает функцию abort(), которая позволяет прервать запрос и указать его тип ошибки, например, 404 или 500. Вот пример:

from  flask import  Flask,  abort

@app.route('/')
def index():
    abort(404)
    # код после выполнения abort() не выполняется

При использовании этой функции будет возвращена стандартная страница ошибки 404, как показано ниже:
функция вернет страницу ошибки 404

abort() может также создавать стандартные страницы для других типов ошибок. Для изменения их оформления применяйте декоратор errorhandler.

Изменение страниц ошибок

С помощью декоратора errorhandler можно создавать собственные страницы для специфичных ошибок. Он принимает параметр - номер ошибки HTTP, для которой создается страница. Давайте откроем файл hooks.py и добавим там кастомизацию для ошибок 404 и 500 с использованием декоратора:

from flask import Flask, request, g, abort
#...
#...
@app.after_request
def after_request(response):
    print("after_request() called")
    return response

@app.errorhandler(404)
def http_404_handler(error):
    return "<p>HTTP 404 Error Encountered</p>", 404

@app.errorhandler(500)
def http_500_handler(error):
    return "<p>HTTP 500 Error Encountered</p>", 500

@app.route("/")
def index():
    # print("index() called")
    # return '<p>Testing Request Hooks</p>'
    abort(404)

if  __name__  ==  "__main__":
#...

Обратите внимание, что каждый обработчик ошибок принимает аргумент error с дополнительной информацией об ошибке.

При открытии корневого URL вы увидите следующую страницу:
кастомная страница ошибки 404

Вопросы для самопроверки:

  1. Какой HTTP статус и content-type назначаются по умолчанию, когда возвращается строка в Flask?
  2. Как можно добавить заголовки в ответ во Flask?
  3. Какой формат кортежей используется для формирования ответа в Flask?
  4. Для чего используется функция redirect() в Flask, и какой статус она создает по умолчанию?
  5. Какие декораторы предоставляет Flask для выполнения действий до или после обработки запроса?