Верстаем статический web-сайт с помощью Python3x, обработка ошибок
Jazz
Опубликован: | 2024-02-14T03:22:47.282533Z |
Отредактирован: | 2024-02-14T03:22:47.282533Z |
Статус: | публичный |
Продолжаем разработку sitefish - web-приложения для статического web-сайта. На текущий момент в проекте всё готово, чтобы наконец-то приступить к вёрстке страниц сайта, но есть один небольшой изъян, устранению которого стоит посвятить отдельный топик. Разрабатываемое приложение в текущем состоянии не умеет корректно обрабатывать ошибки пользователя и в некоторых критических ситуациях прерывает удобный сёрфинг по ссылкам. Обо всём по порядку, приступим...
Восстанавливаем рабочий терминал
Работа над проектом прерывалась, поэтому необходимо восстановить рабочее окружение. Запускаю терминал, вхожу в корневой каталог sitefish и активирую виртуальное окружение.
$ cd ~/workspace/sitefish
$ source venv/bin/activate
После этого действия в тексте приглашения командной строки терминала появился префикс с заключенным в скобки именем активного виртуального окружения - (venv)
. В этом терминале я буду выполнять все последующие в этой демонстрации команды.
Диагностика сбоев
Давайте рассмотрим чуть более детально текущий изъян sitefish, о котором я упомянул выше в преамбуле этой статьи. Запускаю отладочный сервер sitefish.
$ python runserver.py
На текущий момент у нашего будущего сайта есть три ещё не свёрстанные в полной мере страницы - три URL-адреса, по которым отзывается сервер. Для каждого из этих адресов в каталоге templates/main
имеется шаблон с соответствующим именем, а в макете сайта предусмотрены ссылки. Потенциальный посетитель сайта с любой открытой страницы может по этим ссылкам перейти на любую другую страницу. Но... Переходить со страницы на страницу можно не только по ссылкам. У браузера есть адресная строка, и ничто не мешает пытливому пользователю вбить в неё любой URL-адрес сайта, не используя при этом представленные вебмастером ссылки. Что будет, если пользователь введёт в адресную строку следующий адрес: /products.html
?
Хороший вопрос... Давайте ответим на него, воспользовавшись браузером, отладочный сервер запущен и работает на заднем плане. Открываю браузер, вбиваю следующий URL:
localhost:5000/products.html
Жму enter
и...
И вижу сбой отладочного сервера. Статус код 500
сигнализирует о "падении" сервера. То, что мы видим в окне браузера на снимке экрана выше, обычно называют страницей отладчика. Отладчик в sitefish управляется опцией DEBUG
в файле настроек .env
. На "боевом" сервере сети отладчик будет выключен, и пользователь на запрос по предложенному URL-адресу сайта получит стандартную страницу с ошибкой и статус кодом 500
, запрограммированную в настройках http-сервера. На этой странице не будет никаких ссылок. Пользователь выругается и уйдёт на другой сайт. Перспектива вырисовывается не радужная...
Придумываем метод исправления ошибки
Давайте проанализируем информацию, которую предоставил нам отладчик на своей странице в момент сбоя. Кроме статус кода ответа сервера, отладчик сообщил имя класса сработавшего исключения - TemplateNotFound
, что переводится на русский язык как "шаблон не найден", и имя шаблона, который пытался найти и открыть обработчик на данном URL-адресе - main/products.html
. Такого шаблона в структуре каталогов sitefish нет и никогда не было.
Обработчиком для данного URL-адреса является функция представления show_page
в файле main/views.py
. Если в логику этой функции добавить проверку наличия шаблона с заданным в URL-адресе именем, то ошибочные действия пользователя можно подстраховать соответствующими действиями. Например, мы можем изменить статус код исключения на 404
, и предложить для такого исключения новый обработчик с соответствующей логикой, а пользователю показать страницу с элементами макета сайта и путеводными ссылками в этих элементах. Таков план...
Дополняем структуру каталогов sitefish
Итак, мне нужен новый обработчик исключения, и его необходимо разместить где-то в структуре файлов и каталогов sitefish. Создаю новый каталог с именем errors
и в нём файл с именем __init__.py
- в этом файле я размещу обработчик 404
-исключения.
$ mkdir sitefish/errors
$ touch sitefish/errors/__init__.py
Этому новому обработчику будет необходим html-шаблон. Создаю в каталоге templates
вложенный каталог с именем errors
и в нём новый html-файл с именем error.html
.
$ mkdir sitefish/templates/errors
$ touch sitefish/templates/errors/error.html
Таким образом структура файлов и каталогов sitefish останется упорядоченной, и для каждой сущности в этой структуре будет предусмотрено своё место в соответствии с выбранными именами.
Определяем базовые каталоги приложения
На текущий момент все базовые каталоги приложения (base
, templates
, static
) определены в файле __init__.py
в базовом каталоге sitefish
. Пару из этих каталогов мне нужно будет использовать в коде файла main/views.py
и импортировать соответствующие им переменные. Пока эти переменные размещены в __init__.py
, есть риск нарваться на циклический импорт. Поэтому выношу все базовые переменные в новый файл, даю ему имя dirs.py
.
$ vim sitefisth/dirs.py
Вот как будет выглядеть код этого модуля. Весь код я скопировал в буфер и перенёс из файла sitefish/__init__.py
.
import os
from starlette.config import Config
base = os.path.dirname(__file__)
static = os.path.join(base, 'static')
templates = os.path.join(base, 'templates')
settings = Config(os.path.join(os.path.dirname(base), '.env'))
Теперь в соответствующем модуле views.py
я могу без последствий для себя эти переменные импортировать и использовать в логике его функций.
Дополняем обработчик URL-адреса
Открываю в текстовом редакторе файл, в котором хранится код обработчиков URL-адресов подпрограммы main.
$ vim sitefish/main/views.py
В этом файле дописываю пару процедур импорта. Мне нужен стандартный класс исключений Starlette - HTTPException
, и мне нужны переменные, хранящие адреса базовых каталогов в файловой системе сервера - static
и templates
.
...
from starlette.exceptions import HTTPException
...
from ..dirs import static, templates
Используя переменную static
, переписываю функцию представления show_favicon
, на этот раз она будет иметь следующую логику.
async def show_favicon(request):
if request.method == 'GET':
return FileResponse(
os.path.join(static, 'images', 'favicon.ico'))
В функцию представления добавляю дополнительную проверку, существует ли шаблон с переданным в URL-адресе именем.
async def show_page(request):
page = request.path_params.get('page')
if page == 'index.html':
return RedirectResponse(request.url_for('index'), 301)
template = f'main/{page}'
## проверяем, существует ли шаблон с заданным именем
tf = os.path.join(templates, template)
if not os.path.exists(tf):
## если шаблон не существует, генерируем 404-исключение
raise HTTPException(
status_code=404, detail='Такой страницы у нас нет.')
## если шаблон существует, будет выполнен следующий код
return request.app.jinja.TemplateResponse(
template,
{'request': request})
Вот как в итоге выглядит этот файл в моём текстовом редакторе.
Обрабатываем исключение
Теперь мне нужно создать обработчик для только что поднятого в функции show_page
исключения. Открываю в текстовом редакторе следующий файл.
$ vim sitefish/errors/__init__.py
В него пишу вот такой код.
async def show_error(request, exc):
return request.app.jinja.TemplateResponse(
'errors/error.html',
{'reason': exc.detail,
'request': request,
'error': exc.status_code},
status_code=exc.status_code)
В этом коде я использовал шаблон с именем errors/error.html
. Шаблон уже существует, но в нём отсутствует код. Открываю его в текстовом редакторе.
$ vim sitefish/templates/errors/error.html
Пишу в этот файл следующий код.
{% extends "base.html" %}
{% block title %}{{ error }}{% endblock title %}
{% block styles %}
{{ super() }}
{% assets filters='cssmin', output='generic/css/errors/error.css',
'css/base.css' %}
<link rel="stylesheet" type="text/css" href="{{ ASSET_URL }}">
{% endassets %}
{% endblock styles %}
Такой код мы уже видели в файле index.html
, в данном случае я изменил только блок title
и одну строчку в блоке styles
- параметр output
в блоке assets
.
Добавляем обработчик в ASGI-приложение
Открываю в текстовом редакторе файл __init__.py
из базового каталога sitefish
.
$ vim sitefish/__init__.py
Удаляю из кода этого файла все переменные базовых каталогов, они теперь размещены в модуле dirs.py
, дописываю пару процедур импорта в __init__.py
.
...
from .dirs import base, static, templates, settings
from .errors import show_error
...
Создаю словарь для действующих исключений, на текущий момент в нём будет одно единственное исключение с именем 404
.
errs = {404:show_error}
Для этого исключения я задал соответствующий обработчик, код которого разработал выше, - функцию show_error
. Дописываю классу Starlette
ещё один параметр вызова - exception_handlers
.
app = Starlette(
debug=settings.get('DEBUG', cast=bool),
routes=[
Route('/', show_index, name='index'),
Route('/favicon.ico', show_favicon, name='favicon'),
Route('/{page}', show_page, name='page'),
Mount('/static', app=StaticFiles(directory=static), name='static')],
exception_handlers=errs)
Настало время протестировать только что выполненные изменения в коде sitefish.
Тестируем страницу с ошибкой в браузере
Убеждаюсь, что отладочный сервер запущен и работает в штатном режиме, а в его терминале нет выхлопа с ошибками. Перемещаюсь в окно браузера, в адресной строке вбиваю всё тот же URL-адрес - localhost:5000/products.html
- именно на этом адресе была зафиксирована ошибка в предыдущей сессии. Обновляю страницу.
И на этот раз пользователь, проследовав по ошибочному URL-адресу, увидит страницу с типовыми элементами макета sitefish, на странице есть главное меню и путеводные ссылки в нём, а в заголовке страницы указан статус код 404
. Чуть позже я сверстаю окончательный вариант страницы с ошибкой и размещу на ней дополнительную информацию. А цель этой демонстрации на данный момент полностью достигнута.
Git
В предыдущей, посвященной sitefish демонстрации я устанавливал пару пакетов в виртуальное окружение этого web-приложения и не отразил эти изменения в Git. Эту недоработку тоже нужно исправить. Выполняю в терминале следующую команду.
$ pip freeze > requirements.txt
Давайте посмотрим на статус Git-репозитория sitefish.
$ git status
Из полученного выхлопа великолепно видно, какие файлы были отредактированы, а какие были добавлены в процессе работы над кодом в этой демонстрации. Можно сделать очередной commit.
Продолжение следует
Код приложения после всех только что выполненных правок можно посмотреть в моём профиле на github.com по этой ссылке. Все подготовительные действия выполнены, и теперь я могу приступить к главной цели этого цикла статей - вёрстке страниц статического сайта. Об этом я подробно расскажу в следующих выпусках этого блога.
Как всегда.., вопросы, предложения, пожелания и другие отклики можно оставить в комментариях, синяя кнопка ниже работает. А лайки, подписки и донаты благодарных читателей будут дополнительным мотивирующим фактором для автора блога.
Метки: | web, python3x, starlette, sitefish, jinja2, status-code, 404, http-exception |