Автор выбрал Фонд свободного и открытого исходного кода для получения пожертвования в рамках программы Write for DOnations.
Введение
Flask — это легковесный веб-фреймворк на Python, предоставляющий полезные инструменты и функции для создания веб-приложений на языке Python.
Когда вы разрабатываете веб-приложение, неизбежно столкнетесь с ситуациями, когда ваше приложение ведет себя не так, как вы ожидали. Вы можете допустить опечатку в переменной, неправильно использовать цикл for
или составить условие if
таким образом, что возникнет исключение Python, например, вызвать функцию до ее объявления или просто попытаться найти страницу, которой не существует. Вам будет проще и плавнее разрабатывать приложения Flask, если вы научитесь правильно обрабатывать ошибки и исключения.
В этом уроке вы создадите небольшое веб-приложение, демонстрирующее, как обрабатывать общие ошибки, с которыми сталкиваются при разработке веб-приложения. Вы создадите пользовательские страницы ошибок, используете отладчик Flask для устранения исключений и примените журналирование для отслеживания событий в вашем приложении.
Предварительные условия
-
Локальная среда программирования Python 3. Вы можете следовать инструкции для вашего дистрибутива в серии Как установить и настроить локальную среду программирования для Python 3. В этом руководстве мы назовем наш каталог проекта
flask_app
. -
Понимание основных концепций Flask, таких как маршруты, функции-представления и шаблоны. Если вы не знакомы с Flask, ознакомьтесь с руководствами Как создать ваше первое веб-приложение с использованием Flask и Python и Как использовать шаблоны в приложении Flask.
-
Понимание базовых концепций HTML. Вы можете просмотреть нашу серию руководств Как создать веб-сайт с помощью HTML для получения базовых знаний.
Шаг 1 — Использование отладчика Flask
На этом шаге вы создадите приложение с несколькими ошибками и запустите его без режима отладки, чтобы увидеть, как приложение реагирует. Затем вы запустите его с включенным режимом отладки и используете отладчик для устранения ошибок в приложении.
С активированной средой программирования и установленным Flask откройте файл под названием app.py
для редактирования внутри вашего каталога flask_app
:
Добавьте следующий код внутри файла app.py
:
В приведенном выше коде вы сначала импортируете класс Flask
из пакета flask
. Затем вы создаете экземпляр приложения Flask под названием app
. Вы используете декоратор @app.route()
для создания функции-представления под названием index()
, которая вызывает функцию render_template()
в качестве возвращаемого значения, которая, в свою очередь, отображает шаблон под названием index.html
. В этом коде есть две ошибки: первая заключается в том, что вы не импортировали функцию render_template()
, а вторая – что файл шаблона index.html
не существует.
Сохраните и закройте файл.
Далее, сообщите Flask о приложении, используя переменную окружения FLASK_APP
с помощью следующей команды (на Windows используйте set
вместо export
):
Затем запустите сервер приложений с помощью команды flask run
:
Вы увидите следующую информацию в своем терминале:
Output * Serving Flask app 'app' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Этот вывод предоставляет следующую информацию:
-
Файл приложения Flask, который обслуживается (
app.py
в данном случае) -
Окружение, которое здесь обозначено как
production
. Предупреждающее сообщение подчеркивает, что этот сервер не предназначен для развертывания в производственной среде. Вы используете этот сервер для разработки, поэтому можете проигнорировать это предупреждение, но для получения дополнительной информации обратитесь к странице Варианты развертывания в документации Flask. Также вы можете ознакомиться с этим руководством по развертыванию Flask с использованием Gunicorn или этим с uWSGI, либо использовать платформу DigitalOcean App Platform для развертывания вашего приложения Flask, следуя руководству Как развернуть приложение Flask с помощью Gunicorn на App Platform. -
Режим отладки выключен, что означает, что отладчик Flask не запущен, и вы не получите полезных сообщений об ошибках в своем приложении. В производственной среде отображение подробных ошибок подвергает ваше приложение уязвимостям безопасности.
-
Сервер запущен по адресу
http://127.0.0.1:5000/
. Чтобы остановить сервер, используйтеCTRL+C
, но пока не делайте этого.
Теперь посетите главную страницу с помощью вашего браузера:
http://127.0.0.1:5000/
Вы увидите сообщение, которое выглядит следующим образом:
OutputInternal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
Это 500 Internal Server Error, который является ответом сервера об ошибке, указывающим на то, что сервер столкнулся с внутренней ошибкой в коде приложения.
В терминале вы увидите следующий вывод:
Output[2021-09-12 15:16:56,441] ERROR in app: Exception on / [GET]
Traceback (most recent call last):
File "/home/abd/.local/lib/python3.9/site-packages/flask/app.py", line 2070, in wsgi_app
response = self.full_dispatch_request()
File "/home/abd/.local/lib/python3.9/site-packages/flask/app.py", line 1515, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/home/abd/.local/lib/python3.9/site-packages/flask/app.py", line 1513, in full_dispatch_request
rv = self.dispatch_request()
File "/home/abd/.local/lib/python3.9/site-packages/flask/app.py", line 1499, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
File "/home/abd/python/flask/series03/flask_app/app.py", line 8, in index
return render_template('index.html')
NameError: name 'render_template' is not defined
127.0.0.1 - - [12/Sep/2021 15:16:56] "GET / HTTP/1.1" 500 -
Traceback выше проходит через код, который вызвал внутреннюю ошибку сервера. Строка NameError: name 'render_template' is not defined
указывает на корень проблемы: функция render_template()
не была импортирована.
Как видите, здесь вам приходится идти в терминал для устранения ошибок, что не очень удобно.
Вы можете улучшить процесс устранения ошибок, включив режим отладки на вашем сервере разработки. Для этого остановите сервер с помощью CTRL+C
и установите переменную окружения FLASK_ENV
в development
, чтобы запустить приложение в режиме разработки (что включает отладчик), используя следующую команду (на Windows используйте set
вместо export
):
Запустите сервер разработки:
Вы увидите вывод, похожий на следующий, в терминале:
Output * Serving Flask app 'app' (lazy loading)
* Environment: development
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 120-484-907
Здесь вы видите, что теперь среда разработки установлена в development
, отладочный режим включен и отладчик активен. Debugger PIN
— это PIN-код, который вам нужно ввести для разблокировки консоли в вашем браузере (интерактивная оболочка Python, к которой вы можете получить доступ, нажав на маленькую иконку терминала, обведенную на изображении ниже).
Обновите главную страницу в своем браузере, и вы увидите следующую страницу:
Здесь вы видите сообщение об ошибке, отображаемое таким образом, чтобы его было легче понять. Первый заголовок сообщает вам название исключения Python, которое вызвало проблему (NameError
в данном случае). Вторая строка дает вам прямую причину (render_template()
не определено, что означает, что в данном случае оно не импортировано). Далее следует трассировка через внутренний код Flask, который был выполнен. Читайте трассировку снизу вверх, потому что последняя строка в трассировке обычно содержит наиболее полезную информацию.
Примечание:
Обведенная иконка терминала позволяет вам выполнять код Python в браузере на разных фреймах. Это полезно, когда вы хотите проверить значение переменной так же, как вы бы это делали в интерактивной оболочке Python. Когда вы нажмете на иконку терминала, вам нужно будет ввести PIN-код отладчика, который вы получили при запуске сервера. В этом руководстве вам не понадобится эта интерактивная оболочка.
Чтобы исправить эту проблему с NameError
, оставьте сервер запущенным, откройте новое окно терминала, активируйте вашу среду и откройте файл app.py
:
Измените файл следующим образом:
Сохраните и закройте файл.
Здесь вы импортировали функцию render_template()
, которая отсутствовала.
С запущенным сервером разработки обновите главную страницу в вашем браузере.
На этот раз вы увидите страницу ошибки с информацией, которая выглядит следующим образом:
Outputjinja2.exceptions.TemplateNotFound
jinja2.exceptions.TemplateNotFound: index.html
Это сообщение об ошибке указывает на то, что шаблон index.html
не существует.
Чтобы исправить это, вы создадите файл шаблона base.html
, от которого будут наследоваться другие шаблоны, чтобы избежать повторения кода, а затем шаблон index.html
, который расширяет базовый шаблон.
Создайте директорию templates
, которая является директорией, где Flask ищет файлы шаблонов. Затем откройте файл base.html
с помощью вашего любимого редактора:
Добавьте следующий код в ваш файл base.html
:
Сохраните и закройте файл.
Этот базовый шаблон содержит всю HTML-шаблонность, которую вы будете использовать повторно в других шаблонах. Блок title
будет заменяться для установки заголовка каждой страницы, а блок content
будет заменяться содержимым каждой страницы. В панели навигации есть два ссылки, одна для главной страницы, где вы используете вспомогательную функцию url_for()
для ссылки на функцию представления index()
, и другая для страницы About, если вы решите включить её в свое приложение.
Далее откройте файл шаблона под названием index.html
, который будет наследоваться от базового шаблона.
Добавьте следующий код в него:
Сохраните и закройте файл.
В приведенном выше коде вы расширяете базовый шаблон и переопределяете блок content
. Затем вы устанавливаете заголовок страницы и отображаете его в заголовке H1
с помощью блока title
, а также отображаете приветствие в заголовке H2
.
С запущенным сервером разработки обновите индексную страницу в своем браузере.
Вы увидите, что приложение больше не отображает ошибки, и индексная страница отображается так, как ожидалось.
Теперь вы использовали режим отладки и увидели, как обрабатывать сообщения об ошибках. Далее вы прервете запрос, чтобы ответить сообщением об ошибке по вашему выбору, и увидите, как отвечать с помощью пользовательских страниц ошибок.
Шаг 2 — Создание пользовательских страниц ошибок
На этом шаге вы узнаете, как прерывать запросы и отвечать сообщением об ошибке 404 HTTP, когда пользователь запрашивает данные, которых нет на сервере. Вы также узнаете, как создавать пользовательские страницы ошибок для распространенных ошибок HTTP, таких как 404 Not Found
и 500 Internal Server Error
.
Для демонстрации того, как прерывать запросы и возвращать пользовательскую страницу ошибки 404 HTTP, вы создадите страницу, которая отображает несколько сообщений. Если запрашиваемое сообщение не существует, вы ответите ошибкой 404.
Сначала откройте ваш файл app.py
, чтобы добавить новую маршрутизацию для страницы сообщений:
Добавьте следующий маршрут в конец файла:
Сохраните и закройте файл.
В маршруте выше у вас есть переменная URL idx
. Это индекс, который определит, какое сообщение будет отображаться. Например, если URL /messages/0
, будет отображаться первое сообщение (Message Zero
). Вы используете int
конвертер для приема только положительных целых чисел, потому что переменные URL по умолчанию имеют строковые значения.
Внутри функции-представления message()
у вас есть обычный список Python под названием messages
с тремя сообщениями. (В реальном сценарии эти сообщения будут поступать из базы данных, API или другого внешнего источника данных.) Функция возвращает вызов функции render_template()
с двумя аргументами, message.html
как файл шаблона, и переменную message
, которая будет передана в шаблон. Эта переменная будет содержать элемент из списка messages
в зависимости от значения переменной idx
в URL.
Затем откройте новый шаблонный файл message.html
:
Добавьте следующий код в него:
Сохраните и закройте файл.
В приведенном выше коде вы расширяете базовый шаблон и переопределяете блок content
. Вы добавляете заголовок (Messages
) в заголовок H1 и отображаете значение переменной message
в заголовке H2.
С запущенным сервером разработки посетите следующие URL-адреса в своем браузере:
http://127.0.0.1:5000/messages/0
http://127.0.0.1:5000/messages/1
http://127.0.0.1:5000/messages/2
http://127.0.0.1:5000/messages/3
Вы увидите, что в H2
содержится текст Message Zero
, Message One
или Message Two
соответственно на каждом из первых трех URL-адресов. Однако на четвертом URL-адресе сервер ответит сообщением об ошибке IndexError: list index out of range
. В производственной среде ответ был бы 500 Internal Server Error
, но правильным ответом здесь является 404 Not Found
, чтобы указать, что сервер не может найти сообщение с индексом 3
.
Вы можете ответить ошибкой 404
, используя вспомогательную функцию abort()
Flask. Для этого откройте файл app.py
:
Измените первую строку, чтобы импортировать функцию abort()
. Затем измените функцию представления message()
, добавив try ... except
условие как показано в выделенных частях ниже:
Сохраните и закройте файл.
В приведенном выше коде вы импортируете функцию abort()
, которую используете для прерывания запроса и ответа с ошибкой. В функции представления message()
вы используете конструкцию try ... except
для обертывания функции. Сначала вы пытаетесь вернуть шаблон messages
с сообщением, соответствующим индексу в URL. Если индекс не имеет соответствующего сообщения, будет вызвано исключение IndexError
. Затем вы используете предложение except
для перехвата этой ошибки и функцию abort(404)
для прерывания запроса и ответа с HTTP-ошибкой 404 Not Found
.
С запущенным сервером разработки используйте браузер, чтобы снова посетить URL, который ранее отвечал IndexError
(или посетите любой URL с индексом больше 2):
http://127.0.0.1:5000/messages/3
Вы увидите следующий ответ:
Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
Теперь у вас есть более информативное сообщение об ошибке, указывающее, что сервер не смог найти запрошенное сообщение.
Далее вы создадите шаблон для страницы ошибки 404 и для страницы ошибки 500.
Сначала вы зарегистрируете функцию с помощью специального декоратора @app.errorhandler()
в качестве обработчика для ошибки 404
. Откройте файл app.py
для редактирования:
nano app.py
Отредактируйте файл, добавив выделенную часть следующим образом:
Сохраните и закройте файл.
Здесь вы используете декоратор @app.errorhandler()
для регистрации функции page_not_found()
в качестве пользовательского обработчика ошибок. Функция принимает ошибку в качестве аргумента и возвращает вызов функции render_template()
с шаблоном под названием 404.html
. Вы создадите этот шаблон позже, и вы можете использовать другое имя, если хотите. Также вы возвращаете целое число 404
после вызова render_template()
. Это говорит Flask, что код состояния в ответе должен быть 404
. Если вы его не добавите, код состояния по умолчанию будет 200
, что означает, что запрос успешно выполнен.
Далее, откройте новый шаблон 404.html
:
Добавьте в него следующий код:
Сохраните и закройте файл.
Как и любой другой шаблон, вы расширяете базовый шаблон, заменяете содержимое блоков content
и title
, и добавляете свой HTML-код. Здесь у вас есть заголовок <h1>
в качестве титула, тег <p>
с пользовательским сообщением об ошибке, сообщающим пользователю, что страница не найдена, и полезное сообщение для пользователей, которые могли ввести URL вручную.
Вы можете использовать любой HTML, CSS и JavaScript в своих страницах ошибок так же, как и в других шаблонах.
С запущенным сервером разработки, используйте браузер, чтобы снова посетить следующий URL:
http://127.0.0.1:5000/messages/3
Вы увидите, что теперь страница имеет панель навигации, которая находится в базовом шаблоне, и пользовательское сообщение об ошибке.
Аналогично, вы можете добавить пользовательскую страницу ошибки для ваших ошибок 500 Internal Server Error
. Откройте файл app.py
:
Добавьте следующий обработчик ошибок под обработчиком ошибок 404
:
Здесь вы используете тот же шаблон, что и для обработчика ошибок 404
. Вы используете декоратор app.errorhandler()
с аргументом 500
, чтобы сделать функцию под названием internal_error()
обработчиком ошибок. Вы отображаете шаблон под названием 500.html
и отвечаете с кодом состояния 500
.
Затем, чтобы продемонстрировать, как будет представлена пользовательская ошибка, добавьте маршрут, который отвечает с ошибкой 500
HTTP в конце файла. Этот маршрут всегда будет давать ошибку 500 Internal Server Error
, независимо от того, запущен ли отладчик или нет:
Здесь вы создаете маршрут /500
и используете функцию abort()
для ответа с ошибкой 500
HTTP.
Сохраните и закройте файл.
Далее, откройте новый шаблон 500.html
:
Добавьте в него следующий код:
Сохраните и закройте файл.
Здесь вы делаете то же самое, что и с шаблоном 404.html
. Вы расширяете базовый шаблон и заменяете блок содержимого заголовком и двумя пользовательскими сообщениями, информирующими пользователя о внутренней ошибке сервера.
С запущенным сервером разработки, посетите маршрут, который возвращает ошибку 500
:
http://127.0.0.1:5000/500
Ваша пользовательская страница появится вместо общей страницы ошибки.
Теперь вы знаете, как использовать пользовательские страницы ошибок для HTTP-ошибок в вашем приложении Flask. Далее вы узнаете, как использовать логирование для отслеживания событий в вашем приложении. Отслеживание событий помогает вам понять, как ведет себя ваш код, что, в свою очередь, облегчает разработку и устранение неполадок.
Шаг 3 — Использование логирования для отслеживания событий в вашем приложении
На этом шаге вы будете использовать логирование для отслеживания событий, которые происходят, когда сервер запущен и приложение используется, что помогает вам видеть, что происходит в вашем коде приложения, и облегчает устранение ошибок.
Вы уже видели логи всякий раз, когда запускается сервер разработки, которые обычно выглядят так:
127.0.0.1 - - [21/Sep/2021 14:36:45] "GET /messages/1 HTTP/1.1" 200 -
127.0.0.1 - - [21/Sep/2021 14:36:52] "GET /messages/2 HTTP/1.1" 200 -
127.0.0.1 - - [21/Sep/2021 14:36:54] "GET /messages/3 HTTP/1.1" 404 -
В этих логах вы можете увидеть следующую информацию:
127.0.0.1
: Хост, на котором запущен сервер.[21/Сен/2021 14:36:45]
: Дата и время запроса.GET
: Метод HTTP-запроса. В данном случаеGET
используется для получения данных./messages/2
: Путь, запрошенный пользователем.HTTP/1.1
: Версия HTTP.200
или404
: Код статуса ответа.
Эти логи помогают диагностировать проблемы, возникающие в вашем приложении. Вы можете регистрировать больше информации, когда хотите узнать больше деталей о некоторых запросах, используя логгер app.logger
, предоставляемый Flask.
С помощью логирования вы можете использовать различные функции для сообщения информации на разных уровнях логирования. Каждый уровень указывает на событие, произошедшее с определенной степенью серьезности. Могут использоваться следующие функции:
app.logger.debug()
: Для детальной информации о событии.app.logger.info()
: Подтверждение того, что все работает, как ожидалось.app.logger.warning()
: Указание на что-то неожиданное (например, “мало места на диске”), но приложение продолжает работать, как ожидалось.app.logger.error()
: Произошла ошибка в какой-то части приложения.app.logger.critical()
: Критическая ошибка; все приложение может перестать работать.
Чтобы продемонстрировать, как использовать логгер Flask, откройте файл app.py
для редактирования, чтобы залогировать несколько событий:
Измените функцию представления message()
, чтобы она выглядела следующим образом:
Сохраните и закройте файл.
Здесь вы зарегистрировали несколько событий на разных уровнях. Вы используете app.logger.info()
для регистрации события, которое работает как ожидается (это уровень INFO
). Вы используете app.logger.debug()
для подробной информации (уровень DEBUG
), указывая, что приложение теперь получает сообщение с определенным индексом. Затем вы используете app.logger.error()
для регистрации того, что было вызвано исключение IndexError
с конкретным индексом, который вызвал проблему (уровень ERROR
, потому что произошла ошибка).
Посетите следующий URL:
http://127.0.0.1:5000/messages/1
Вы увидите следующую информацию в терминале, где запущен ваш сервер:
Output
[2021-09-21 15:17:02,625] INFO in app: Building the messages list...
[2021-09-21 15:17:02,626] DEBUG in app: Get message with index: 1
127.0.0.1 - - [21/Sep/2021 15:17:02] "GET /messages/1 HTTP/1.1" 200 -
Здесь вы видите сообщение INFO
app.logger.info()
и сообщение DEBUG
с номером индекса, которое вы зарегистрировали с помощью app.logger.debug()
.
Теперь посетите URL для сообщения, которое не существует:
http://127.0.0.1:5000/messages/3
Вы увидите следующую информацию в терминале:
Output[2021-09-21 15:33:43,899] INFO in app: Building the messages list...
[2021-09-21 15:33:43,899] DEBUG in app: Get message with index: 3
[2021-09-21 15:33:43,900] ERROR in app: Index 3 is causing an IndexError
127.0.0.1 - - [21/Sep/2021 15:33:43] "GET /messages/3 HTTP/1.1" 404 -
Как видите, у вас есть логи INFO
и DEBUG
, которые вы видели раньше, и новый лог ERROR
, потому что сообщение с индексом 3
не существует.
Регистрация событий, подробной информации и ошибок помогает вам определить, где что-то пошло не так, и облегчает устранение неполадок.
Вы узнали на этом этапе, как использовать логгер Flask. Ознакомьтесь с Как использовать логирование в Python 3 для лучшего понимания логирования. Для более глубокого изучения логирования см. документацию по логированию Flask и документацию Python по логированию.
Заключение
Теперь вы знаете, как использовать режим отладки в Flask, и как устранять и исправлять некоторые распространенные ошибки, с которыми вы можете столкнуться при разработке веб-приложения на Flask. Вы также создали пользовательские страницы ошибок для распространенных HTTP-ошибок и использовали логгер Flask для отслеживания событий в вашем приложении, что поможет вам проверять и выяснять, как ведет себя ваше приложение.
Если вы хотите узнать больше о Flask, ознакомьтесь с страницей темы Flask.
Source:
https://www.digitalocean.com/community/tutorials/how-to-handle-errors-in-a-flask-application