Верстаем статический web-сайт с помощью Python3x, обработка ошибок

Jazz

Опубликован:  2024-02-14T03:22:47.282533Z
Отредактирован:  2024-02-14T03:22:47.282533Z
Статус:  публичный
9
0
0

Продолжаем разработку 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 и...

NEntgwYxSG.png

И вижу сбой отладочного сервера. Статус код 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})

Вот как в итоге выглядит этот файл в моём текстовом редакторе.

RKX8bnhNYe.png

Обрабатываем исключение

Теперь мне нужно создать обработчик для только что поднятого в функции 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 - именно на этом адресе была зафиксирована ошибка в предыдущей сессии. Обновляю страницу.

tNLaHZWmlb.png

И на этот раз пользователь, проследовав по ошибочному URL-адресу, увидит страницу с типовыми элементами макета sitefish, на странице есть главное меню и путеводные ссылки в нём, а в заголовке страницы указан статус код 404. Чуть позже я сверстаю окончательный вариант страницы с ошибкой и размещу на ней дополнительную информацию. А цель этой демонстрации на данный момент полностью достигнута.

Git

В предыдущей, посвященной sitefish демонстрации я устанавливал пару пакетов в виртуальное окружение этого web-приложения и не отразил эти изменения в Git. Эту недоработку тоже нужно исправить. Выполняю в терминале следующую команду.

$ pip freeze > requirements.txt

Давайте посмотрим на статус Git-репозитория sitefish.

$ git status

S5GitSrEA5.png

Из полученного выхлопа великолепно видно, какие файлы были отредактированы, а какие были добавлены в процессе работы над кодом в этой демонстрации. Можно сделать очередной commit.

Продолжение следует

Код приложения после всех только что выполненных правок можно посмотреть в моём профиле на github.com по этой ссылке. Все подготовительные действия выполнены, и теперь я могу приступить к главной цели этого цикла статей - вёрстке страниц статического сайта. Об этом я подробно расскажу в следующих выпусках этого блога.

Как всегда.., вопросы, предложения, пожелания и другие отклики можно оставить в комментариях, синяя кнопка ниже работает. А лайки, подписки и донаты благодарных читателей будут дополнительным мотивирующим фактором для автора блога.