Razor – новый механизм визуализации в ASP.NET

 

Одним из проектов, над которым работала моя команда, был новый механизм визуализации (view engine) для ASP.NET.

В платформе ASP.NET MVC всегда поддерживалась концепция «механизма визуализации», который представляет собой заменяемые модули, реализующие выбор различного синтаксиса шаблона. Сегодня, стандартный механизм визуализации для ASP.NET MVC использует аналогичные файлы, что и ASP.NET Web Forms – .aspx/.ascx/.master. Существуют и другие популярные механизмы визуализации для ASP.NET MVC – Spark и NHaml.

Новый движок, над которым мы работаем, оптимизирован под генерацию HTML-кода, фокусируясь на коде шаблона. Кодовое имя для данного движка – «Razor», первая бета-версия будет доступна уже скоро.

Цели разработки

Во время разработки прототипа и оценки Razor, было выделено несколько основных целей:

Компактный , выразительныйигибкий. Razor уменьшает «нагрузку на клавиатуру» при редактировании файла, обеспечивает быстрый и гибкий процесс программирования. В отличие от большинства синтаксиса шаблонов, вам не нужно прерывать написание кода, чтобы явно определить серверные блоки кода в HTML. Анализатор достаточно умен, для того чтобы сделать это за вас, основываясь на коде. Вы получаете компактный и выразительный синтаксис, который легко и весело набирать.

Простота освоения. Razor прост в освоении и позволяет за короткий срок стать более продуктивным с минимальным количеством усилий. Вы используете все существующие навыки языка программирования и HTML.

Не новый язык. Мы сознательно выбрали путь, который не основывается на новом императивном языке для Razor. Вместо этого, мы позволили разработчикам использовать уже существующие навыки работы с C#/VB(или другим языком) и предоставили синтаксис разметки шаблона, который обеспечивает потрясающий процесс построения HTML, оставляя выбор языка за вами.

Работает в любом текстовом редакторе. Razor не требует определенного инструмента для редактирования и позволяет быть продуктивным в любом старом текстовом редакторе (даже в блокноте прекрасно работает)

Поддержка Intellisense. Несмотря на то, что Razor разрабатывался с учетом отсутствия привязки к определенному инструменту или редактору кода, у него будет отличная поддержка в Visual Studio. Visual Studio 2010 и Visual Web Developer 2010 скоро получат обновления для Intellisense.

Возможность модульного тестирования. Новая реализация механизма рендеринга будет поддерживать возможность модульного тестирования (без контроллера или веб-сервера, можно размещать в любом проекте модульного тестирования – не требуются специальные домены приложений).

Последние несколько месяцев мы занимались разработкой приложения, используя данную технологию. Провели исследования юзабилити с различными добровольцами, включая группы не .NET-разработчиков. Отзывы от людей были превосходными.

Выбор и гибкость

Самой прекрасной возможностью у ASP.NET, является замена буквально любой части кода. Если определенный механизм не удовлетворяет вашим потребностям, можно легко заменить его собственной реализацией.

Со следующей версией у ASP.NET MVC появится новое диалоговое окно «Add->View», которое позволит вам выбрать синтаксис для нового файла шаблона представления. Оно позволит легко выбирать любой доступный механизм рендеринга, установленный на локальной машине:

image

Razor будет присутствовать в списке, так как он будет интегрирован непосредственно в ASP.NET MVC. Все вспомогательные методы и модели программирования будут доступны, как в Razor, так и в механизме рендеринга .ASPX.

У вас будет возможность сочетать и сравнивать шаблоны представлений, написанные с использованием различных механизмов в рамках одного приложения или сайта. Например, вы можете создать одни представления, используя .aspx, другие с .cshtml или .vbhtml (расширения Razor-файлов, для C# и VB соответственно), третьи со Spark или NHaml. Мало того, у вас шаблон представления на одном механизме может частично использовать представление, написанное для другого механизма. Вы капитан и выбираете то, что нужно.

Hello World и Razor

Razor позволяет начать со статического HTML-кода (или любого текстового содержимого), который постепенно становится динамическим, путем добавления серверного кода. Одна из основных целей проектирования Razor’a была предоставить гибкий процесс кодирования и позволить быстро интегрировать серверный код в HTML-разметку с минимумом усилий.

Чтобы увидеть простой пример, давайте создадим пример “hello world” и выведем следующее сообщение:

image

Как это выглядит при использовании .ASPX-кода

Если мы собираемся построить, представленный выше пример, используя ASP.NET и существующую разметку синтаксиса .ASPX, нам потребуется использовать <%= %> блоки, для определения областей кода внутри HTML-разметки:

image

Хотелось бы поделится одним наблюдением – каждая область кода, требует пяти символов (<%= %>) для начала и конца последовательности. Некоторые из данных символов (в особенности клавиша %, которая на большинстве клавиатур располагается в верхней центральной части) не совсем просто набирать.

Как это выглядит с Razor-синтаксисом

Обратите внимание на начало области кода в Razor, где используется символ @. В отличии от <% %>, Razor не требует явно закрывать область кода:

image

Анализатор Razor умеет семантически распознавать C#/VB в областях кода, вот почему нам не нужно явно закрывать блок кода. Razor может определить вышеуказанные утверждения, как самостоятельные области кода и неявно закрыть их для нас.

Даже в таком тривиальном примере мы уберегли себя от 12 лишний нажатий клавиш, которые нам пришлось бы делать раньше. Набрать символ @ на клавиатуре гораздо проще, чем символ %.

Циклы и вложенный HTML

Давайте рассмотрим другой случай, когда вам нужно вывести список продуктов (и цену напротив каждого):

image

Создаем пример с .ASPX

Если мы собираемся реализовать пример, используя ASP.NET и существующую разметку синтаксиса .ASPX, нам потребуется код, который будет динамически генерировать <ul>-список с элементами <li> для каждого продукта:

image

Создаем пример с Razor

Ниже показано, как сгенерировать эквивалентное представление, используя Razor:

image

Обратите внимание, что мы начали цикл foreach, используя символ @, а далее разместили строку HTML-содержимого с блоками кода внутри. Так как анализатор Razor понимает семантику C# в наших блоках, он может определить, что содержимое <li> должно быть внутри foreach и рассматривает его, как содержимое, которое должно быть зациклено. Он также распознает закрывающую скобку «}» и останавливает foreach.

Razor также достаточно умен, чтобы распознать выражения @p.Name и @p.Price внутри элемента <li> как серверный код и выполнить его для каждого прохода цикла. Обратите внимание, Razor автоматически закрывает области кода @p.Name и @p.Price основываясь на совместном использовании HTML и кода.

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

Блоки if и многострочные выражения

Ниже представлено несколько примеров распространенных ситуаций:

If выражения

Как и в примере с foreach, вы можете располагать содержимое внутри if выражений (или других языковых структур C# или VB), при этом явно не указывая начало/конец областей кода. Например:

image

Многострочные выражения

Вы можете обозначать несколько строчек кода обернув их в блок @{ код } следующим образом:

Обратите внимание, что переменные могут быть разбиты на несколько серверных блоков кода, переменная «message» объявлена в многострочном блоке @{ }, а также использована в блоке кода @message. Точно так же, как и в синтаксисе .aspx-файлов <% %> и <%= %>.

Комплексные выражения

Синтаксис @( ) позволяет размещать комплексные выражения. Например, можно переписать указанный выше код для конкатенации строки и числа вместе, внутри блока @ ( код ):

image

Интеграция содержимого и кода

Анализатор Razor знает множество особенностей языка, позволяя полностью доверится ему, а не проводить время за собственной реализацией.

Конфликтует ли он с email-адресами и другими местами использования @ в HTML?

Анализатор Razor достаточно умен, чтобы в большинстве случаев определить, используется ли символ @ в коде или в статическом содержимом. Например, ниже я используя символ @, как часть email-адреса:

image

Во время разбота файла, Razor анализирует содержимое справа от символа @ и пытается определить, является ли эта часть C# кодом (если это CSHTML файл) или VB (если это VBHTML файл), или же это статические данные. Приведенный выше код, выведет следующий HTML (где email-адрес выведется, как статическое содержимое, а @DateTime.Now определится, как код):

image

В случае, где содержимое может интерпретироваться как код, а вы хотите вывести его как статический контент, достаточно явно указать два символа @@ вместо одного.

Определение вложенного содержимого

Размещая HTML-содержимое внутри if/else, foreach или других блочных выражений, следует обернуть внутреннее содержимое HTML- или XML-элементов для лучшего определения начала области содержимого.

Например, ниже, я обернул многострочный блок содержимого (который включает в себя фигурные скобки) в тег <span>:

image

Это сгенерирует следующий код для клиента, включая элемент <span>:

image

По желанию, вы можете обернуть вложенное содержимое в блок <text> в случаях, когда данные должны сгенерироваться для клиента без тега обертки:

image

Данный код сгенерирует следующее содержимое для клиента. Обратите внимание – никаких обворачивающих тегов:

image

Кодирование HTML

По умолчанию содержимое, использующее блок @, автоматически кодировано в HTML для защиты против XSS-атак.

Макет/MasterPage – основы

Очень важно иметь единообразный вид на всех страницах веб-сайта или приложения. ASP.NET 2.0 представил концепцию «мастер страниц», которые помогают добиться этого при использовании .aspx-страниц или шаблонов. Razor также поддерживает данную концепцию, но применяя концепцию «layout pages»(страницы макета), которые позволяют определять шаблон сайта, а далее наследовать от него представления/страницы.

Простейший пример макета

Ниже представлен простой пример страницы макета, который мы сохраним под названием «SiteLayout.cshtml». Он может содержать любой статический HTML, а также любой динамический серверный код. Далее добавим вызов вспомогательного метода «RenderBody()» в том месте, где мы хотим вывести определённое тело содержимого для запрашиваемого URL:

image

Далее можно создать шаблон представления, назвав его «Home.cshtml», которое содержит только нужный код или содержимое для построения определенного тела запрашиваемой страницы и основывается на шаблоне макета:

image

Заметьте, что вы явно задали свойство «LayoutPage» в коде нашего файла Home.cshtml. Это означает, что мы хотим использовать шаблон SiteLayout.cshtml, как макет для данного представления. Мы можем так же задать файл макета непосредственно в контроллере ASP.NET MVC, указывая Home.cshtml, как шаблон представления или же настроив его стандартным макетом для сайта (в данном случае мы определяем его в одном файле проекта и все шаблоны представлений подхватывают его автоматически).

Когда мы сгенерируем Home.cshtml, как шаблон представления, он объединит содержимое макета и подстраницы, и отошлет содержимое клиенту:

image

Компактный, чистый и выразительный код

Хотелось бы обратить внимание на код выше и заметить, что определение макета и его использование из представлений/страниц имеет маленький размер. Код на скриншоте содержит все содержимое файлов SiteLayout.cshtml и Home.cshtml, нет никаких дополнительных настроек или тегов, нет префикса %@Page % и другой разметки или заданных свойств.

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

Макет/MasterPage – добавляем заменяемые части

На страницах макета можно определять различные «секции», которые изменяют общее представление собственным содержимым. Это позволяет легко изменять/заполнять прерывающиеся секции содержимого на странице макета и обеспечивает значительную гибкость сайта.

Например, можно вернуться к нашему SiteLayout.cshtml и определить две секции внутри макета, которые могут изменять общий вид страницы. Назовем эти части «menu» и «footer» и укажем, что заполнять их необязательно, передав параметр optional=true помощнику RenderSection() (мы делаем это, используя новые необязательные параметры в C#, о которых я уже рассказывал).

image

Так как две секции помечены, как «optional», я не обязан определять их в файле Home.cshtml. Мой сайт продолжает прекрасно работать, даже если их там нет.

Давайте вернемся обратно в Home.cshtml и объявим собственные части Menu и Footer. Скриншот ниже, содержит весь код Home.cshtml, больше ничего не потребуется. Однако, я перенес настройки LayoutPage на глобальный уровень, так что их здесь больше нет.

image

Наши собственные секции перезаписывают «menu» и «footer» с помощью объявленных в файле блоков @section. Мы решили не требовать обертывать содержимое «main/body» внутри секции, а вместо этого просто оставить её в строковом виде (это позволяет сократить количество нажатий на клавиши и с легкостью добавлять секции в макет без просмотра всех страниц и изменения их синтаксиса).

Когда мы снова сгенерируем Home.cshtml как шаблон представления, он объединит содержание макета и подстраницы, интегрируя две новых части страницы и вернет клиенту следующее:

image

Инкапсуляция и повторное использование HTML-помощников

Мы рассмотрели, как обеспечить единый вид веб-сайта, используя страницы макета. Давайте теперь рассмотрим, как создавать повторно используемые вспомогательные методы HTML, которые дают нам чистую инкапсуляцию для генерации HTML-содержимого, и которые мы можем использовать в рамках одного или даже нескольких сайтов.

Программные HTML-помощники

Сегодня у ASP.NET MVC в наличии есть вспомогательные методы, которые можно вызвать в блоках кода и которые инкапсулируют генерацию HTML. И реализованы они только в виде кода (чаще всего, как методы расширений). Все существующие методы расширения HTML, созданные с ASP.NET MVC, будут так же работать при использовании Razor:

image

Декларативные HTML-помощники

Генерация HTML на основе одного класса кода работает, но не является идеальной.

Одной из возможностей в Razor является простой путь для создания повторно используемых HTML-помощников, используя более декларативный подход. Наш план заключается в том, чтобы дать вам возможность объявлять повторно используемые помощники с помощью декларативного синтаксиса @helper { }, как показано ниже.

image

Вы сможете размещать .cshtml-файлы, которые содержат данные помощники в каталоге Views\Helpers и впоследствии заново использовать их в любом представлении или странице на вашем сайте (никаких дополнительных действий не потребуется):

image

Обратите внимание, как помощник ProductListing() определяет аргументы и параметры. Вы можете передавать любые параметры, какие захотите (включая необязательные параметры, типы, допускающие значения NULL, обобщенные методы и т.п.). Вы также сможете отлаживать данный код в Visual Studio.

Важно: @helper синтаксис не будет доступен в первой бета-версии Razor, мы надеемся он будет доступен в следующих версиях. Вспомогательные методы, оформленные в виде кода, будут работать сразу.

Передача строковых шаблонов, как параметров

Другая полезная (и нереально мощная) возможность, которую мы предоставляем с Razor – это возможность передавать параметры в виде «строковых шаблонов» во вспомогательные методы. Данные «строковых шаблонов» могут содержать в себе, как HTML, так и код, и могут быть вызваны по требованию вспомогательного метода.

Ниже вы видите пример данной функциональности в действии, я использую HTML-помощник «Grid», который генерирует DataGrid для клиента:

image

Вызов метода Grid.Render – это C#. Мы используем новые именуемые параметры в C# для передачи строго типизированных аргументов в метод Grid.Render, данная возможность дает нам полное завершение выражений с Intellisense и проверку во время компиляции.

Параметр «format», который мы передаем, когда объявляем столбец, является «строчным шаблоном», он содержит и HTML и код, мы также можем настроить под себя формат данных. А самое прекрасное – то, что помощник Grid’a может вызывать наш строчный шаблон, как метод делегата, и может вызывать его, когда нужно и столько раз, сколько потребуется. В данном случае он будет вызывать каждый раз, когда он генерирует строку сетки и передает в «элемент», который может использовать наш шаблон для отображения результата.

Данная возможность позволит разрабатывать более мощные вспомогательные методы HTML. Вы сможете реализовывать их, как в коде (так вы это делаете сегодня), так и используя декларативный подход @helper{ }.

Поддержка в Visual Studio

Как я уже замечал ранее, одной из основных целей Razor – минимизировать количество нажатий клавиш, а также легко редактировать код любым текстовым редактором. Мы постарались держать синтаксис чистым, компактным и простым.

Несмотря на это, мы спроектировали Razor так, чтобы можно было получить все удобства редактирования кода в Visual Studio. Мы предоставим полную поддержку HTML-, JavaScript- и C#/VB-кода в Intellisense в Razor-файлах:

image

Обратите внимание каким образом отработал Intellisense для объекта Product на «@p.» внутри элемента <li>. Заметьте, в Solution Explorer в папке \Views присутствуют оба шаблона представлений .aspx и .cshtml. Вы можете использовать различные механизмы рендеринга в одном приложении, чтобы понять, какой синтаксис вам более по душе.

Выводы

Мы думаем, Razor предоставляет новый изящный движок представлений для ориентированных на код шаблонов. Процесс программирования станет быстрым и веселым. Синтаксис является компактным и уменьшает количество нажатий клавиш, в то же время, улучшая читабельность разметки и кода. В следующей версии ASP.NET MVC механизм будет встроен в систему. Вы сможете удалить файлы .cshtml/.vbhtml в своем приложении и запустить их, как отдельные страницы, что также позволяет вам использовать их в приложениях ASP.NET Web Forms.

Все разработчики, которые тестировали проект последние несколько месяцев, оставили крайне позитивные отзывы. Мы собираемся предоставить публичную бета-версию, как можно скорее и, конечно же, ожидаем услышать ваши мнения.

Оригинал