Pull to refresh

Альтернатива .NET MVC и WEB Forms

Reading time 6 min
Views 11K
Как известно, разработка веб-приложений для .NET использует в основном два решения — WebForms и .NET MVC. Наверняка существует что-то еще, но эти два решения от Microsoft — владельца платформы, веб-сервера и среды разработки.

Собственно, наличие только этих двух решений в .NET удобно тем, что ярко демонстрирует разницу между компонентным подходом (WebForms) и MVC. На других платформах тот же расклад, но на PHP, например, такое множество фреймворков, что не всегда можно понять, к какому типу данный фреймворк относится.

Сравнительный анализ вышеуказанных двух .NET решений легко нагуглить, поэтому останавливаться нет смысла.

Пожалуй, главная проблема WebForms — сложность управления HTML-кодом и как следствие проблемы для фронт-энд разработчиков. Сей недостаток “исправили” с помощью .NET MVC, переложив на программиста полное управление, со всеми вытекающими последствиями.

Предполагаю возмущение евангелистов MVC, но тем не менее выскажу свое убеждение: MVC в вебе — как на корове седло. Разумеется, речь не об общей идее разделения кода, представления и данных. Речь о типовой имплементации паттерна в вэбе — контроллеры, экшены и собственно вся архитектура.

Вообще MVC паттерн был придуман для десктопа, и там он не заменял компоненты, а применялся для построения архитектуры приложения.

Одна из существенных проблем MVC, как и других некомпонентных решений, — отсутствие обеспечения персистентности (сохранения состояния элементов) страницы между вызовами с браузера. В старом добром WebForms мы ставили на форму, например, чекбокс и не волновались как он будет отрисован и как будет восстановлено его состояние после перезагрузки. То есть программист занимался тем, чем ему положено заниматься — реализацией бизнес-логики, взаимодействуя с компонентами через их бек-энды, и обрабатывая события элементов страницы после действий пользователя. По сути то же, как в десктопе.

MVC фреймворки не обеспечивают это автоматически, посему надо состояние страницы восстанавливать вручную, и на сложных страницах (например, каталог с корзиной, фильтрами отбора товаров и т.д.) эта задача не является тривиальной.

Самый простой пример для демонстрации казалось бы надуманной проблемы с персистентностью. Некая страница с табличными данными. Вверху страницы форма с фильтром, например, диапазон дат и кнопка «Поиск». Нажимаем кнопку. Вызывается экшен с параметрами, грузится модель, вывели на страницу. Отлично. Далее — кликаем, например, на ссылке пагинатора или заголовку столбца для сортировки. Вызывается соответствующий экшен с параметрами… ой, а где ж наш диапазон дат? Вернемся назад, запишем его в сессии (повесим все элементы на ссылку пагинатора и т.д.). И это только простой, хотя и повседневный, пример. Отдельная проблема сложных страниц представления: одно огромное с огромным контроллером и моделью, или разбиение на модули, виджеты, и как тогда все это комбинировать?

Есть разные способы решить проблемы. Например:

— большие модели, включающие всю страницу, что есть абсурд, потому как модель — это, прежде всего, модель бизнес-данных, а не свалка с данными страницы;
— всякого рода иерархические MVC типа маленькие MVC в одном большом. Даже комментировать этот кошмар не стоит;
— ViewState как в WebForms — это еще куда ни шло, но там есть свои, не раз описанные, проблемы;
— асинхронные обращения к серверу без перезагрузки страниц вплоть до так называемых SPA страниц.

Последний вариант — гораздо более трудоемок в разработке, но, тем не менее, стал весьма популярным, хотя для этого нет ни малейших оснований. Правда жизни в том, что пользователю абсолютно все равно как там что загружается. Более того: пользователя, как правило, раздражает, что страница типа загрузилась, но что-то там еще не загружено и непонятно когда и как уже будет загружено. Плюс тормоза браузера, особенно при использовании клиентских javascript фреймворков, и особенно на мобильных устройствах. Конечно, подобные проблемы в основном мучают разработчиков во всяких Кремниевых Долинах. Для наших пользователь или заказчик — валенок, который сам не понимает что хочет, и наша задача не делать, как он требует, а объяснить ему, что на самом деле ему надо совсем другое (как правило, то, что мы уже умеем написать). А сомневается — пусть погуглит что в этом сезоне ангуляры и всякие реакты — последний писк, тренд и вообще.

Но тут напрашивается вопрос: для кого умные люди придумали облачные вычисления, если некоторые запихивают логику на клиента (аргумент в виде экономии десятка килобайт при нынешних скоростях инета и копеечном железе неубедителен)?

На самом деле эти мучения (а точнее извращения) делаются только по одной причине — обойти проблему персистентности элементов страницы. То есть придумали костыль в виде MVC, затем вместо того, чтобы вылечить конечности и отказаться от костыля, просто-напросто добавили ему еще одну пару — клиентские javascript фреймворки.

Разумеется, есть сайты типа соцсетей или гугледока где, как основной функционал, нужен асинхронный обмен данными, сообщениями и всякими уведомлениями, но процент таких сайтов среди всего множества оных примерно как процент цукербергов среди всех программистов.

Что, на мой скромный взгляд, следовало бы сделать: взять компонентное решение и разрулить проблему управления HTML кодом, не усложняя работу программисту. Попытки подобных решений есть на других платформах. Например PHP фреймворк Prado, но там разработчики наступили на другие грабли — начали создавать всякие кастомные тэги для фронт-энда, всякого рода и иже с ними. Впрочем, Razor с его “птичьим” языком тоже не приводит верстальщиков в восторг.

Пример нужного нам решения — Java фреймворк Wicket, на основе которого был сделан PHP фреймворк Zippy. По сути, были портированы основные архитектурные решения. В частности, в качестве фронт-энда используется чистый HTML и, само собой, событийно-ориентированная компонентная архитектура.

Останавливаться на Zippy не будем — у проекта есть домашняя страница и репозитарий на GitHub. Поэтому возвращаемся к тому с чего начали — альтернативе существующих решений для .NET — фреймворку ZippyNet, как результату портирования из PHP (Wicket) на .NET.

Фреймворк не требует ни WebForms, ни .NET MVC библиотек. Исходники на GitHub. Дополнительно в проект входит простой сайт-заготовка для демонстрации конфигурации и работы фреймворка и страничка с некоторыми элементами, как пример использования.

Вкратце принцип работы.

Фронт-энд:

<a zippy="link1">click me</a> <span zippy="label1" ></span>

Бек-энд:

namespace TestApp.Pages
{
 public class MainPage : ZippyNet.Html.WebPage
 {
 public MainPage ()
 {
 
 Label label1 = new Label("label1", "Hello");
 ClickLink link1 = new ClickLink("link1");
 
 //назначаем серверный обработчик 
 link1.setClickHandler((HtmlComponent sender) =>
 {
 label1.setText("I am clicked");
 });
 
 // добавляем элементы к классу страницы.
 this.add(link1);
 this.add(label1);


 }
 }
}

Как это работает.

Бек-энд и фронт-энд связываются с помощью идентификаторов label1, link1 в специальном атрибуте zippy. Используется пассивная шаблонизация. При рендеринге страницы экземпляр класса соответствующего компонента находит свой тэг в DOM модели (используется библиотека CsQuery) и изменяет его значение или атрибуты в соответствии со своими данными. При этом, для элементов-источников событий формируются специального вида адреса HTTP запросов, по которым сервер понимает какой из элементов был кликнут или переключен, то есть, является источником события (при первом обращении к странице, когда создается экземпляр ее класса, URI содержит имя класса страницы). И при этом не нужен никакой ViewState — экземпляр класса страницы с дочерними компонентами и, соответственно, их данными, хранится в серверной сессии.

И кстати, по поводу AJAX. Несмотря на то, что необходимость в асинхронных вызовах при использовании данного фреймворка отпадает, тем не менее ряд компонентов поддерживают асинхронную работу для тех случаев, когда это необходимо. Причем асинхронная работа обеспечивается тем же компонентным подходом. Разработчику не нужно ничего программировать и тем более прилеплять всякие, прости Господи, ангуляры и реакты.

Все, что нужно сделать — заменить обработчик на setAjaxClickHandler. Компонент link1 при рендеринге шаблона страницы на сервере добавит обработчик onclick с AJAX запросом. Компонент label1 при рендеринге асинхронного ответа ответит яваскриптом, изменяющим его текстовое значение на клиенте новым текстом (что то типа $(‘#label1’).text(‘I am AJAX clicked’) ), который выполнится с помощью eval(). Ничего дополнительно кодировать не нужно.

При этом следует заметить, что AJAX запросы, как и обычные синхронные, выполняются в контексте экземпляра страницы (с доступом к состоянию всех элементов страницы и их данным) — в отличие от MVC контроллера который пересоздаеться каждый раз.

В этом и суть компонентов — они сами обеспечивают сохранение своего состояния, рендеринг интерфейса и обработку событий, в зависимости от контекста. Программист сосредотачивается на бизнес-логике а верстальщик работает с чистым HTML. Компоненты обеспечивают модульность проекта и, как следствие, масштабируемость разработки. Компоненты легко расширяются простым наследованием от соответствующего класса с перегрузкой необходимых методов. Например, компонент ввода даты наследует функционал элемента ввода текста (для тега ), но при рендеринге дополнительно формирует строку яваскрипта, назначающую тэгу плагин DatePicker из JQuery UI. А при обработке запроса с сервера преобразует принятые значения в DataTime.

На первый взгляд, статья — пиар некоей наколенной поделки, но в этом нет особого смысла. В отличие от большинства «велосипедистов» я реалист и знаю что использовать свои велосипеды буду только я сам — собственно для того их и писал чтобы выполнять свои проекты, которые дают мне кусок хлеба, в разы быстрее и проще.

Но мне хотелось бы обратить внимание сообщества, особенно новичков, убежденных что ООП и MVC это одно и тоже, что несмотря на наличие распиаренных модных фишек и всякого рода “трендов”, зачастую существуют гораздо более простые логичные и естественные решения и не всегда стоит бежать как стадо леммингов только потому что это направление указано некими гуру — теоретиками программирования или потому что гугл пиарит очередной JavaScript фреймворк.
Tags:
Hubs:
-5
Comments 45
Comments Comments 45

Articles