Библиотека сайта rus-linux.net
Фреймворк Yesod
Глава 22 из книги "Архитектура приложений с открытым исходным кодом", том 2.
Оригинал: Yesod
Автор: Michael Snoyman
Перевод: Н.Ромоданов
22.5. Yesod
Если мы посмотрим на типичную парадигму «Модель-Представление-Контроллер» («Model-View-Controller» - MVC), то Persistent является моделью, а Shakespeare является преставлением. Тогда все, что остается Yesod, это выступать в роли контроллера.
Самая основная особенность Yesod это - маршрутизация. Она позволяет иметь декларативный синтаксис и типобезопасную диспетчеризацию обращений. На основе этого в Yesod создано много других возможностей: генерация потокового контента, виджеты, интернационализация, статические файлы, формы и аутентификация. Но основной особенностью, которая добавлена в Yesod, в действительности является маршрутизация.
Это многоуровневый подход облегчает пользователям обмениваться различными компонентами системы. Некоторых не интересует использование Persistent. Для них в ядре системы нет ничего, даже упоминающего о Persistent. Точно также, хотя аутентификация и сохранение файлов со статистикой используются часто, эти функции нужны не каждому.
С другой стороны, многие пользователи хотят пользоваться всеми этими функциями. И делают это, включая все оптимизации, имеющиеся в в Yesod, что не всегда просто. Чтобы упростить процесс, в Yesod также предлагается специальный инструментальный набор, с помощью которого настраивается базовая часть сайта с наиболее часто используемыми возможностями.
Маршруты
Учитывая то, что маршрутизация на самом деле является основной функцией Yesod, давайте начнем с нее. Синтаксис маршрутизации очень прост: шаблон ресурса, имя и методы запросов. Например, простой блог-сайт может выглядеть следующим образом:
/ HomepageR GET /add-entry AddEntryR GET POST /entry/#EntryId EntryR GET
В первой строке определена домашняя страница. Здесь говорится - «Я представляю собой корневой каталог домена, мое имя HomepageR и я отвечаю на запросы GET». Завершающий символ «R» в именах ресурсов является просто общепринятым соглашением, в нем не закладывается никакого общего смысла, кроме как просигнализировать разработчику о том, что нечто является маршрутом.
Во второй строке определена страница добавочной записи. На этот раз мы отвечаем на оба запроса GET и POST. Вы удивитесь, почему в Yesod, в отличие от большинства фреймворков, требуется, чтобы вы явно указывали методы ваших запросов. Причина в том, что Yesod старается придерживаться принципов RESTful настолько, насколько это возможно, а запросы GET и POST по смыслу действительно очень различные. Мало того, что вы устанавливаете эти два метода по отдельности, затем вы отдельно определяете для них функции обработчиков. В действительности, это дополнительная функция в Yesod. Если вы хотите, вы можете убрать из списка названия методов и функции ваших обработчиков будут использоваться для всех методов запроса.
Третья строка немного интереснее. После второго «слеша» у на есть #EntryId
. Таким образом определяется параметр EntryId
. Мы уже ссылались на эту функцию в разделе о компоненте Persistent: Yesod будет автоматически строить компонент, представляющий собой путь к соответствующему значению ID. Предположим, что на серверной стороне у нас используется SQL (о Mongo поговорим позже), и если пользователь сделает запрос /entry/5
, то функции обработчика будет вызвана с аргументом EntryId 5
. Но если пользователь сделает запрос /entry/some-blog-post
, то Yesod вернет значение 404.
Очевидно, что это также возможно в большинстве других веб фреймворков. Например, в подходе, применяемом в Django, можно использовать регулярные выражения для проверки соответствия маршрутов, например, r"/entry/(\d+)"
. Однако подход, применяемый в Yesod, имеет ряд преимуществ:
- Ввод «EntryId» семантически гораздо более удобен/дружественен для разработчика , чем регулярное выражение.
- С помощью регулярных выражений нельзя выразить все (или по крайней мере, нельзя это сделать лаконично). В Yesod мы можем использовать
/calendar/#Day
; вы хотите набрать регулярное выражение для того, чтобы сравнивать даты в ваших маршрутах? - Yesod также автоматически находит для нас пути. В случае использования календаря, наша функция обработки получит значение
Day
. В эквиваленте в Django, функция получит кусок текста, по которому нужно будет выполнять поиск самостоятельно. Это утомительно, это требуется каждый раз повторять и это неэффективно. - До сих пор мы предполагали, что идентификатор базы данных ID является простой строкой цифр. Но что, если он более сложен? В MongoDB, например, используются идентификаторы GUID. В Yesod ваш запрос
#EntryId
все равно будет работать, а система типов проинструктирует Yesod, как анализировать маршрут. В системе регулярных выражений, вам придется пройти по всем вашим маршрутам и заменить\d+
на чудовищно сложное регулярное выражение, необходимое для сравнения идентификаторов GUID.
Адреса типобезопасных URL
Такой подход к маршрутизации порождает одну из самых мощных функций Yesod: типобезопасные адреса URL. Вместо того, чтобы объединять вместе части текста со ссылками, обозначающими маршрут, каждый маршрут в вашем приложении может быть представлен с помощью значения языка Haskell. Благодаря этому сразу исчезает большое количество ошибок 404 Not Found (404 Не найдено): просто невозможно получить неправильный URL. (Все еще можно сформировать URL, который приведет к ошибке 404, например, из-за ссылки на несуществующее сообщение в блоге. Тем не менее, все адреса будут сформированы правильно).
Так как же это магия работает? В каждом сайте есть тип данных route, и для каждого шаблона ресурсов есть свой собственный конструктор. Для нашего предыдущего примера мы получим что-то вроде следующего:
data MySiteRoute = HomepageR | AddEntryR | EntryR EntryId
Если вы хотите перейти по ссылке на домашнюю странице, вы используете HomepageR
. Чтобы разместить ссылку на конкретную запись, вы должны использовать конструктор EntryR
с параметром EntryId
. Например, чтобы создать новую запись и перейти на нее, вы могли бы написать:
entryId <- insert (Entry "My Entry" "Some content") redirect RedirectTemporary (EntryR entryId)
Во всех языках Hamlet, Lucius и Julius есть встроенная поддержка таких типобезопасный адресов URL. Внутри шаблона на языке Hamlet вы можете легко создать ссылку на страницу с дополнительной записью:
<a href=@{AddEntryR}>Create a new entry.
Что же самое интересное? Точно также, как и в случае с сущностями Persistent, компилятор обеспечит вам полный порядок. Если вы поменяли какие-либо из ваших маршрутов (например, вы хотите включить в число ваших маршрутов год и месяц), Yesod заставит вас повсюду в коде обновить каждую ссылку.
Далее: Обработчики и виджеты