(restas:mount-submodule -mysubmodule- (#:mymodule middleware1 middleawre2)Здесь middleware1 и middleware2 это функции, которые должны принимать маршрут и возвращать другой.
..)
В момент построения дерева маршрутов (а это происходит каждый раз при вызове функции restas:recconect-all-routes) для конкретного субмодуля строиться список обрабатываемых им маршрутов и пропускается через цепочку middleware-вызовов. middleware-функция может как угодно изменить маршрут или вообще вернуть другой. Специально для поддержки этого я добавил в cl-routes новый класс routes:proxy-route (который реализует известный паттерн proxy).
Скажем, с помощью модуля restas-directory-publisher можно публиковать директории со статикой:
(restas:mount-submodule -tmp- (#:restas.directory-publisher)Такой код позволит любому просматривать содержимое директории /tmp на сервере, но кто знает что там может быть. Теперь с помощью middleware можно защитить содержимое этого каталога, требуя от посетителей пройти HTTP-авторизацию:
(restas.directory-publisher:*baseurl* '("tmp"))
(restas.directory-publisher:*directory* #P"/tmp/)
(restas.directory-publisher:*autoindex* t))
(defclass http-auth-route (routes:proxy-route) () )Здесь определяется новый класс http-auth-route, наследующий от routes:proxy-route, и для него специализируется метод routes:route-check-conditions, который вызывается для проверки соответствия маршрута условиям запроса. Если маршрут проходит все проверки, то проверяется прошёл ли пользователь HTTP-авторизацию. Функция http-auth-middleware используется для создания таких маршрутов и указывается в списке middlewares макроса restas:mount-submodule.
(defmethod routes:route-check-conditions ((route http-auth-route) bindings)
(if (call-next-method)
(multiple-value-bind (user password) (hunchentoot:authorization)
(or (and (string= user "hello")
(string= password "world"))
(hunchentoot:require-authorization)))))
(defun http-auth-middleware (route)
(make-instance 'http-auth-route :target route))
(restas:mount-submodule -tmp- (#:restas.directory-publisher http-auth-middleware)
(restas.directory-publisher:*baseurl* '("tmp"))
(restas.directory-publisher:*directory* #P"/tmp/)
(restas.directory-publisher:*autoindex* t))
Для маршрутов, наследующих от routes:proxy-route, имеет смысл определять специализации методов routes:route-check-conditions и/или restas:process-route.
Чуть более сложный пример использования описанных возможностей можно найти здесь.
Интересная ссылка на Clack. Неожиданно узнаешь, что и такое уже сделано.
ОтветитьУдалитьСама модель что "application is just a function" не очень убедительна для меня, потому что фактически аргументов этой ф-ции является весь здоровый environment и возвращает эта функция весь environment с возможными модификациями. Но окончательных выводов я не делаю, потому что недостаточно близко знаком с этой моделью - не пробовал ей пользоваться или понять, что именно она упрощает по сравнению с другими моделями.
Вопрос про RESTAS middleware - мне слегка подозрительно, что вещь, которая проверяет авторизацию называется "маршрут" (здесь тоже говорю с позиции поверхностного знакомства - только прочитал этот пост).
> Неожиданно узнаешь, что и такое уже сделано.
ОтветитьУдалитьНу оно, мягко говоря, ещё очень сырое )
> вещь, которая проверяет авторизацию называется
> "маршрут"
В RESTAS обработка запросов производится в маршрутах. Сами маршруты собираются в одно дерево. Диспетчер ищёт наиболее подходящий запросы маршрут. В-первую очередь по шаблону URL. Но маршрут может накладывать также дополнительные условия, более конкретные (например, тип запроса).
Авторизация обычно должна быть привязана к конкретному маршруту, поскольку не все запросы надо проверять (например, какой-нибудь style.css можно отдавать всем желающим), а бывает это операцией затратной. Это как наиболее удобный способ, мне так кажется. Вообще, более подробно про маршруты здесь: http://restas.lisper.ru/ru/manual/routes.html
Меняем килобайты кода на включение мозга :)
ОтветитьУдалить> http://restas.lisper.ru/ru/manual/routes.html
ОтветитьУдалитьЭту страницу я раньше читал, но сам RESTAS я не исипользовал, поэтому и говорю, что знаю поверхностно (хотя прочнение этой сраницы оказалось мне полезным в моем коде для одной задачи на джаве).
> В RESTAS обработка запросов производится в маршрутах.
Вот это мне и подозрительно или, другими словами, смущает. В ментальной модели, которая у меня сложилась, я представлял, что маршрут определяет соответствие УРЛа и обработчкика. Т.е. обработка делается в не в маршруте а в обработчике.
Поэтому информация о том, что проверка авторизации делается в маршруте, немножно диссонирует с представлением, которое у меня сложилось.
Попробую придумать аргумент: можно один обработчик связать с разными маршрутами; если это операция редактирования данных, то в каком бы маршруте она не использовалась, она должна требовать аутентификации.
Возможно, что мне нужно сделать усилие и уточнить мое представление. Поэтому мой коментарий на не претендует истинность.
Маршрут - это сложившаяся терминология, кстати вполне удобная. И нет такого что обработка "делается в маршруте". Маршрут определяет некоторые условия, при соблюдении которых вызывается обработчик этого маршрута. И можно специфицировать одну из обобщеных ф-ий для дополнительной обработки условия. Так что никакого противоречия.
ОтветитьУдалитьНо возможно, автор меня в чём то поправит.
З.Ы. А вообще это демагогия, надо обязательно всё пробовать. Я предлагаю вам весьма наглядные примеры идущие в комплекте.
Демагогия или нет, но слово маршрут мне понятее если оно отнсится к УРЛам (потому что УРЛы похожи на пути в файловой системе, т.е. путь или способ достижения узла в дереве). Если это обобщается/расширяется до "некоторых условий", то уже слово маршрут не так хорошо подходит. Я не предлагагаю вводить дополнительные сущности или переименовывать что-то, просто замечание. Делюсь своими мыслями.
ОтветитьУдалить@Anton Vodonosov
ОтветитьУдалитьС самого начал использования термина routes (в RoR) это был не просто конкретные маршруты, а шаблоны, т.е. он содержали в себе переменные параметры. Плюс обычно к ним добавляют тип запроса, а также есть какие-то возможности для ограничения значений параметров (например, только цифры).
Естественным образом концепция routes обощается до "шаблон URL + набор дополнительных условий". А библиотека cl-routes вообще ничего не знает об обработчиках, маршрут там это отдельный объект, находящийся в общем дереве, плюс инструменты для поиска подходящего маршрута в это дереве. Что делать с найденным маршрутом решает конкретное приложение.
Понятно.
ОтветитьУдалитьТ.е. можно сказать, что Middleware применяется именно для композиции условий выбора обработчика для запроса.
@Anton Vodonosov
ОтветитьУдалитьНе только для композиции. Также можно использовать для изменения результатов обработки запросов, например для добавления заголовков, для пост-обработки, да как угодно. middleware даёт возможность "обернуть" оригинальный маршрут и изменить его поведение.
@Anton Vodonosov
ОтветитьУдалитьВообще решил вместо middleware использовать более адекватный символ "декоратор".