Прежде чем идти
дальше, нам нужно разобраться со способами формирования ответа сервера во Flask. Что это вообще
такое? Когда пользователь запрашивает ту или иную страницу, то браузер получает
данные в виде:
<заголовок
ответа><HTML-документ>
В этом заголовке
находится «служебная» информация, сообщающая браузеру, как интерпретировать
принятые данные. Например, там указывается код ответа:
-
200
– все нормально;
-
404
– страница не найдена;
-
301
– выполнено перенаправление с другого URL;
-
401
– доступ запрещен
и так далее. Кроме
того, в заголовке в параметре content-type прописывается тип данных:
-
text/html
-
text/plain
-
image/jpeg
-
audio/mp4
-
multipart/form-data
Все это имеет
большое значение при разработке сайтов. На этом занятии мы как раз и поговорим
о работе с этим заголовком.
Обратите
внимание, заголовок ответа сервера не имеет ничего общего с заголовком HTML-страницы, то
есть, с содержимым тега
<head></head>
это разные вещи.
Итак, во Flask заголовок в
ответ на запрос можно формировать тремя способами:
- В обработчике
возвратить строку, тогда автоматически ответом будет content-type=text/html и
код 200.
- Сформировать
ответ с помощью функции make_response()
- Возвратить кортеж формата:
(response, status, headers) или (response, headers)
Первый способ
используется наиболее часто, например, вот в такой упрощенной программе:
from flask import Flask, render_template
app = Flask(__name__)
menu = [{"title": "Главная", "url": "/"},
{"title": "Добавить статью", "url": "/add_post"}]
@app.route("/")
def index():
return render_template('index.html', menu=menu, posts=[])
if __name__ == "__main__":
app.run(debug=True)
Функция
представления главной страницы возвращает текст (после обработки шаблона) и
сервер (Flask) автоматически
формирует ответ с параметрами:
-
content-type=text/html
-
code
= 200
Функция make_response
Однако, если мы
хотим самостоятельно определить некоторые параметры заголовка, то можно
воспользоваться специальной функцией
res_obj
= make_response(res_body, status_code=200)
которая
возвращает ссылку на объект ответа. Здесь:
-
res_body – передаваемое
содержимое (контент);
-
status_code – код ответа
сервера (по умолчанию 200).
Предположим, что
мы хотим отправить браузеру страницу в виде обычного текста. Для этого создадим следующий ответ:
@app.route("/")
def index():
content = render_template('index.html', menu=menu, posts=[])
res = make_response(content)
res.headers['Content-Type'] = 'text/plain'
res.headers['Server'] = 'flasksite'
return res
Смотрите,
здесь параметр 'Content-Type' принимает
значение 'text/plain', которое
указывает браузеру отображать данные в виде обычного текста. После обновления
главной страницы, увидим следующее:
Вся страница
отображена в виде текста. Или, можно сделать так:
@app.route("/")
def index():
img = None
with app.open_resource( app.root_path + "/static/images/ava.png", mode="rb") as f:
img = f.read()
if img is None:
return "None image"
res = make_response(img)
res.headers['Content-Type'] = 'image/png'
return res
Мы здесь
загружаем изображение из подкаталога static/images и отдаем его
браузеру, указывая, что принятые данные следует представлять в виде png изображения. И,
действительно, при обновлении страницы, увидим загруженное изображение:
Или, можно
вернуть страницу с определенным кодом, отличным от 200, например:
@app.route("/")
def index():
res = make_response("<h1>Ошибка сервера</h1>", 500)
return res
Здесь
генерируется ошибка 500 – внешняя ошибка сервера с заголовком h1. Вот так
заголовок кардинально влияет на отображение данных в окне браузера.
С помощью
функции make_response можно также передавать информацию для cookies браузера, то
есть, информация, которая будет храниться в браузере клиента и передаваться
серверу при каждом очередном запросе к нашему сайту. Но подробнее об этом мы
поговорим на следующем занятии.
Создание ответа с помощью кортежей
Последний способ
создать ответ – использовать кортежи в одном из следующих форматов:
-
(response,
status, headers)
-
(response,
headers)
-
(response,
status)
response —
строка, представляющая собой тело ответа, status — код состояния HTTP, который
может быть указан в виде целого числа или строки, а headers — словарь со
значениями заголовков. Например, так:
@app.errorhandler(404)
def pageNot(error):
return ("Страница не найдена", 404)
Мы здесь
формирует ответ и указываем код ответа – 404 страница не найдена. Или, так:
@app.route("/")
def index():
return "<h1>Main Page</h1>", 200, {'Content-Type': 'text/plain'}
Здесь после
оператора return записан тоже
кортеж только без круглых скобок. Последний параметр headers – словарь из
набора параметров заголовка.
Создание 301 и 302 редиректов
Очень часто при
развитии сайта некоторые его страницы переносятся на другой URL-адрес. И, чтобы
не потерять позиции этих страниц в поисковой выдаче, поисковым системам нужно
явно указать, что страница перемещена либо временно, либо постоянно на новый URL. Это делается с
помощью перенаправления с кодами:
-
301
– страница перемещена на другой постоянный URL-адрес;
-
302
– страница перемещена временно на другой URL-адрес.
Чтобы во Flask выполнить
перенаправление с прежнего URL на новый, можно использовать функцию
redirect(location, status)
о которой мы уже
говорили. В данном случае ее можно применить так:
@app.route('/transfer')
def transfer():
return redirect(url_for('index'), 301)
Перехват запросов
Последнее, о чем
мы поговорим на этом обзорном занятии – это перехват запросов. Что это такое? В
веб-приложениях часто нужно исполнить определенный код до или после запроса. И,
как мы видели, в частности, это используется для установления соединения с БД. Нам
в каждой функции представления приходилось осуществлять подключение к БД,
следующим образом:
@app.route("/")
def index():
db = get_db() # функции
dbase = FDataBase(db) # подключения к БД
return render_template('index.html', menu=dbase.getMenu(), posts=dbase.getPostsAnonce())
И это не самое
лучшее решение, оно нарушает один из основополагающих принципов
программирования:
DRY –
Don’t Repeat Yourself (не повторяйся)
Так вот, чтобы
вынести за скобки этот общий код, можно воспользоваться специальными
декораторами для перехвата запросов:
-
before_first_request
– выполняет функцию до обработки первого запроса;
-
before_request
– выполняет функцию до обработки текущего запроса;
-
after_request
– выполняет функцию после обработки запроса (такая функция не вызывается при
возникновении исключений в обработчике запросов);
-
teardown_request (похож на after_request) – вызванная
функция всегда будет выполняться вне зависимости от того, возвращает ли
обработчик исключение (ошибку) или нет.
Давайте для
примера добавим эти обработчики в нашу программу и посмотрим последовательность
их вызовов:
@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.teardown_request
def teardown_request(response):
print("teardown_request() called")
return response
Обратите
внимание, первые две функции только выполняют определенные действия, не
возвращая никакого значения, а последние две – принимают объект response и возвращают
его. Запустим эту программу и при переходе на главную страницу сайта увидим
следующие строки в консоли приложения:
before_first_request() called
before_request() called
after_request()
called
teardown_request()
called
Обновим страницу
и теперь увидим только три вызова:
before_request() called
after_request() called
teardown_request()
called
При последующих
вызовах (любых страниц) функция before_first_request вызвана уже не
будет.
В последующих
занятиях, развивая наш тестовый сайт, мы воспользуемся декоратором before_request, чтобы
настраивать связь с БД до выполнения самого запроса.