Python: Веб-разработка (Flask)
Теория: Middlewares
При разработке веб-приложений разработчики сталкиваются с задачами, которые нужно решать на каждом этапе обработки запроса. Например, если у нас есть несколько маршрутов, которые требуют аутентификации, или если нам нужно логировать все входящие запросы, то реализация этой логики в каждом маршруте может привести к дублированию кода и усложнению поддержки приложения. Это также увеличивает вероятность ошибок, так как любой измененный маршрут необходимо будет обновить в нескольких местах. Использование мидлвар позволяет решить эти проблемы.
Мидлвара - это функции, которые обрабатывают запросы и ответы в веб-приложениях. Они выполняются между получением запроса от клиента и отправкой ответа. Они могут выполнять различные задачи, такие как проверка прав доступа, логирование или преобразование данных в ответе
В этом уроке мы рассмотрим, как создавать и использовать мидлвары в Flask, а также изучим примеры их применения
Оборачивание
Прежде чем перейти к изучению непосредственно мидлвар, разберем их принцип действия на примере обычных функций.
Представим, что у нас есть функция, которая прибавляет 5 к аргументу:
Наша задача расширить возможности этой функции, при этом не меняя саму функцию. Например, мы хотим прибавлять 5 к удвоенному значению аргумента. Чтобы решить эту задачу, мы можем написать новую функцию, которая будет внутри себя использовать уже существующую функцию:
Точно так же мы можем расширить и вторую функцию:
В общем случае расширение функции выглядит так:
Чтобы расширить поведение функции, нужно создать новую функцию, которая будет использовать исходную. Главное условие — интерфейсы, то есть сигнатуры, обеих функций должны совпадать. Тогда мы можем заменить в коде вызов одной функции на вызов другой, обернутой, и код продолжит работать без изменений, поскольку интерфейс остался прежним. Код, использующий новую функцию, не заметит, что он обернут и его не потребуется переписывать. Этот подход также называется декорированием.
По похожей идее работают мидлвары. Они расширяют функционал обработчиков запросов, оборачивая их в подобные методы. При этом оригинальный обработчик остается неизменным.
Использование мидлвар
Чтобы использовать мидлвары в Flask, не нужно ничего устанавливать, фреймворк имеет встроенную поддержку мидлвар. Для добавления мидлвар используются декораторы @before_request или @after_request. Декоратор @before_request используется для указания кода, который должен быть выполнен до запроса, а @after_request — после.
Рассмотрим пример кода с подключением мидлвары:
В приведенном примере подключена мидлвара, которая для каждого входящего запроса выводит в консоль сообщение с указанием пути. Также добавлена мидлвара, которая выполняется после обработки запроса и выводит в консоль сообщение, информируя о том, что ответ был успешно отправлен клиенту. Во Flask в мидлваре @after_request обязательно нужно вернуть объект response.
Каждый раз, когда мы используем @before_request или @after_request, очередная мидлвара добавляется в общую цепочку. Каждый запрос, отправляемый на обработку в приложение, проходит через всю цепочку этих мидлвар
Flask по умолчанию выполняет все before мидлвары по порядку, как они заданы в коде. Но если какая-то из них возвращает значение, то на ней закончится вызов, и вернется response.
Если же ни одна из before мидлвар не совершила return, то выполнение дойдет до обработчика маршрута.
Затем, в конце, выполнится after мидлвара.
В примере выше мы подключили мидлвары, одна из которых выводит в консоль сообщение, а другая добавляет кастомный заголовок X-Custom-Header в каждый ответ.
Запустим сервер и сделаем запрос:
При этом в консоли, где запущено приложение, мы можем увидеть вывод:
Обработчики
Самое интересное в мидлварах, что обработчики конкретных маршрутов — это тоже мидлвары. Но в отличие от мидлвар, которые выполняются для всех запросов, обработчики привязаны к конкретному маршруту.
Терминальная мидлвара
Далеко не всегда мы хотим двигаться вглубь по всей цепочке добавленных мидлвар. Например, хотим создать мидлвару для проверки наличия определенного параметра в запросе, не меняя при этом сам обработчик. Если параметр отсутствует, мы можем сразу отправить ответ с ошибкой и пропустить выполнение остальных мидлвар и обработчиков. В таком случае нам нужно сделать return в мидлваре. После возврата движение по цепочке прекратится, все оставшиеся мидлвары пропускаются и происходит отправка ответа:
У такого поведения, когда есть цепочка функций и любая из них в процессе обработки может принять решение остановки цепочки и возврата ответа, есть имя. Такие цепочки называют chain responsibility, и это тоже паттерн
Помимо этого существует мидлвары выполнямые после отправки ответа - teardown_request(). Эта мидлвара выполнится даже в случае, если во время выполнения программы сгенерируется и не будет обработано исключение.
В консоли, где запущено приложение, мы можем увидеть, что сообщение из мидлвары все равно выведется, и лишь затем будет стектрейс ошибки:

