Flask – функциональный фреймворк, который, несмотря на свою компактность, предоставляет много возможностей. В том числе, и работа с серверами: передача запросов и получение ответов. Фреймворк дает возможность создавать ответ сервера тремя способами, а осуществлять перехват запросов с использованием специальных декораторов. Сегодня поговорим более подробно о том, как осуществлять взаимодействие сервера и клиента с помощью Flask в среде Python.
Ответ сервера
Flask дает возможность реализовать ответ сервера такими способами:
- С помощью строки или с использованием шаблонизатора.
- Создание специального объекта, отвечающего за ответ.
- Использование кортежа в формате (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
А этот код показывает, как правильно возвращать ошибку 404 с использованием этой функции.
@app.route('/') def http_404_handler(): return make_response("<h2>404 Error</h2>", 400)
Также веб-приложения могут настраивать куки. Этот процесс становится максимально простым благодаря функции make_response(). Приведем пример кода, который добавляет два куки в браузере, выступающем роль клиента.
@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
Срок действия этих куков будет вплоть до окончания сессии в браузере. Разработчик может указать собственную дату экспирации (то есть, окончания срока действия), передав в качестве третьего аргумента метода 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
Этот фрагмент кода задает срок хранения куки в течение 15 дней.
Использование кортежей для создания ответов
И последний способ создания ответов – использование неизменяемых последовательностей значений (кортежей). Они должны соответствовать определенному формату.
(response, status, headers) (response, headers) (response, status)
response – это строка, которая служит телом ответа. А код состояния HTTP записывается в элементе кортежа status.
Значения заголовков содержатся в 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': 'https://localhost:5000/login'}
С помощью функции представления пользователь перенаправляется на http://localhost:5000/login с использованием ответа 302. Поскольку перенаправление пользователей происходит довольно часто, во Flask есть специальная функция, отвечающая за это – redirect().
from flask import Flask, redirect @app.route('/transfer') def transfer(): return redirect("https://localhost:5000/login")
По предустановленным параметрам, функция redirect() выполняет 302 редиректы. Если же необходимо 301, то соответствующий код нужно указать во втором аргументе этой функции.
Перехват запросов
Веб-приложения нередко требуют выполнения определенного кода до или после запроса. Например, ставится задача вывести весь перечень IP-адресов пользователей, использующих программу или выполнить авторизацию пользователя, а потом показать ему скрытые страницы.
Flask предлагает такие декораторы, чтобы избавить от необходимости копировать постоянно один и тот же код в рамках каждой функции представления:
- before_first_request. С помощью этого декоратора, еще до того, как будет обработан первый запрос, выполняется функция.
- before_request. Выполнение функции происходит до обработки запроса.
- after_request. Сначала обрабатывается запрос, а потом выполняется функция. Она не будет вызываться при возникновении исключений в обработчике запросов. Функцией должен быть принят объект ответа, а потом возвращен тот же или новый ответ.
- teardown_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>Testings 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.
abort() для других типов ошибок покажет похожие страницы.
Если стоит задача изменить внешний вид страниц с ошибками, используется декоратор errorhandler.
Как изменять страницы ошибок
Возможно создание и пользовательской страницы с ошибками. Для этого используется декоратор errorhandler. В качестве аргумента он принимает ошибку, для которой создается специальная страница.
Давайте откроем файл hooks.py, чтобы создать произвольные страницы ошибок.
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>Testings Request Hooks</p>' abort(404) if __name__ == "__main__": #...
Следует отметить то, что все обработчики в качестве аргумента принимают аргумент error, в котором содержатся дополнительные сведения о типе ошибки.
Выводы
Таким образом, работа с запросами и ответами во Flask является простой и интуитивно понятной. Поэтому разобраться в этом по силам даже новичку, который только приступил к изучению веб-программирования.
Как видим, Python – универсальный язык, который может быть использован для создания любых программ для любых платформ.