[Перевод] Полное руководство по использованию ASP.NET Core 1.0 (aka ASP.NET 5) Tag Helpers
Тег-хэлперы (Tag Helpers) — новая функция MVC, которую удобно использовать для генерации HTML кода. Они выглядят как обычные HTML элементы и атрибуты, но обрабатываются движком Razor на стороне сервера. Тег-хэлперы во многом представляют собой альтернативный синтаксис для HTML Helpers, но также они позволяют сделать то, что было трудно или невозможно сделать с помощью HTML Helpers. У каждого тег-хэлпера свое поведение и возможности. Эта статья рассмотрит базовые тег-хэлперы, существующие в MVC 6 (ASP .NET Core 1.0, как стало известно совсем недавно).
Добавление тег-хэлперов для использования в представлении
Если вы создаете новый MVC 6 проект с помощью Visual Studio, то встроенные тег-хэлперы будут доступны по умолчанию. Можно добавить тег-хэлперы из любой сборки / пространства имен, используя директиву @addTagHelper в cshtml файлах.
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
Чтобы добавить тег-хэлперы глобально для всего проекта, добавьте директиву @addTagHelper в файл Views/_ViewImports.cshtml.
Link и Script
Как видно из их названий, эти тег-хэлперы созданы для упрощения добавления тегов link и script в создаваемую HTML разметку. Например, с их помощью можно упростить добавление ссылок на большое число файлов в девелопмент среде, использование CDN с откатом на локальные файлы и инвалидацию кеша.
Рассмотрим пример, в котором мы хотим добавить ссылки на все js файлы в определенной папке (рисунок 1).
Мы можем это легко сделать с помощью тег-хэлпера Script, добавив все файлы по шаблону:
В результате будет сгенерирована следующая HTML разметка:
В данном примере мы использовали шаблон /app/**/*.js, потому что нам нужно было включить еще и js файлы из подпапок. Если же мы хотим добавить ссылки на все js файлы из одной папки, то используем шаблон /app/*.js.
Наряду с атрибутом asp-src-include существует и атрибут asp-src-exclude. Например, в предыдущем примере мы можем сделать так, чтобы ссылки на js файлы в подпапке services не были добавлены:
Тег-хэлпер Link работает точно так же, только с атрибутами asp-href-include и asp-href-exclude.
Теперь рассмотрим атрибуты этих тег-хэлперов, которые помогут подключать CDN. Преимущества использования CDN для хостинга файлов таких популярных фреймворков как jQuery и Bootstrap очевидны: уменьшается нагрузка на наши сервера и потенциально увеличивается производительность у клиента (например, актуальная версия файла уже может быть в кеше браузера у клиента). Среди минусов — нужно предусмотреть возможность того, что CDN не отвечает и нам нужно отдавать файлы со своего сервера. С помощью тег-хэлперов это сделать проще:
Атрибуты asp-fallback-test позволяют указать свойства или объекты, которые нужно проверить для того, чтобы узнать, был ли загружен файл или нет. Если нет, то файл будет загружен с использованием пути, заданном в атрибуте asp-fallback-href. Сгенерированная HTML разметка:
Cache busting (инвалидация кеша) — механизм, добавляющий идентификатор версии файла (на основе его содержимого) к имени файла для css и js файлов. В этом случае можно указать браузеру, что эти файлы можно кешировать на неопределенно долгое время, в случае их изменения имя у них также изменится. Для того чтобы включить этот механизм, достаточно добавить атрибут asp-append-version:
Сгенерированный HTML:
Environment
Тег-хэлпер Environment используется (обычно вместе с тег-хэлперами Link и Script) для генерирования различной HTML разметки в зависимости от среды: девелопмент vs тест vs продакшен. Этот тег-хэлпер используется следующим образом: оберните в этот тег часть HTML кода и укажите среду или среды, для которых эта часть разметки должна быть включена в выходной файл. В следующем примере в девелопмент среде мы подключаем все css файлы по отдельности, а в средах Staging и Production используем скомбинированную минифицированную версию.
В девелопмент среде будет сгенерирован следующий HTML код:
В средах Staging и Production будет добавлена ссылка на минифицированную версию:
Сам тег environment не посылается клиенту. Его значение задается переменной Hosting: Environment. В ASP.NET Core 1.0 (MVC 6) Hosting: Environment используется для тех же целей, для каких раньше использовалась Debug / Release конфигурация. Обычно Hosting: Environment принимает значения: Development, Staging и Production. Значением по умолчанию при запуске сайта из Visual Studio является Development. В этом можно убедиться, открыв свойства MVC проекта в Visual Studio (рисунок 2).
Для того, чтобы протестировать среды Staging или Production, нужно создать новый профиль отладки (Debug Profile) и установить требуемое значение Hosting: Environment. Например, рассмотрим, как создать профиль Production (рисунок 3).
- Кликнуть правой кнопкой мыши по проекту в обозревателе решений и выбрать Свойства.
- Выбрать вкладку Debug (Отладка).
- Нажмите New… для создания нового профиля и назовите его «IIS Express — Prod».
- В выпадающем списке Launch выберите «IIS Express».
- Добавьте новую переменную окружения с именем «Hosting: Environment» и значением «Production».
- Сохраните проект.
Теперь в выпадающем списке Run в Visual Studio можно выбрать профиль «IIS Express — Prod» (рисунок 4).
Form
Именно в этом разделе тег-хэлперы делают свою работу лучше всего, увеличивая читаемость и простоту cshtml представлений. В качестве примера рассмотрим простую логин форму со следующей моделью:
public class LoginViewModel
{
public string UserName { get; set; }
public string Password { get; set; }
}
Теперь посмотрим, как можно сгенерировать поле ввода для свойства UserName.
@Html.EditorFor(l => l.UserName)
Когда мы используем HTML хэлпер, то вызываем обычный C# метод, который возвращает некоторую HTML разметку. С тег-хэлперами мы имеем дело сразу с обычной HTML разметкой, в которой встречаются MVC специфичные атрибуты. В результате обоих подходов на выходе мы получим один и тот же сгенерированный HTML код:
Почему подход с тег-хэлперами лучше? Код с ними становится читаемее и проще. Например, мы хотим добавить к полю ввода класс:
@Html.EditorFor(l => l.UserName, new { htmlAttributes = new { @class = "form-control" } })
В данном случае код с использованием тег-хэлперов однозначно выигрывает и в красоте, и в простоте.
Теперь приведем код всей формы логина в двух вариантах.
@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { role = "form" }))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
@Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.UserName, "", new { @class = "text-danger" })
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
}
Как мы видим, версия с тег-хэлперами выглядит проще и читаемее. Особенно мне нравится отсутствие выражения using для генерации формы, мне всегда это казалось каким-то хаком. Еще стоит отметить, что нам теперь не нужно явно добавлять AntiForgeryToken на форму. Тег-хэлпер form делает это автоматически, если не отключить это поведение атрибутом asp-anti-forgery=«false».
Конечно, Visual Studio подсвечивает атрибуты тег-хэлперы, чтобы их легко можно было отличить от стандартных HTML атрибутов (рисунок 5):
Внутри атрибутов asp-for работает IntelliSense, подсказывающий имена свойств модели (рисунок 6).
Теперь рассмотрим атрибуты тег-хэлпера form. Например для того, чтобы связать форму с определенным действием определенного контроллера, используются атрибуты asp-controller и asp-action:
В результате будет сгенерирована следующая HTML разметка:
Как мы видим, по умолчанию добавляется AntiForgeryKey, а методу формы присваивается значение POST. Это поведение можно изменить, добавив в разметку переопределение атрибута method. Все HTML атрибуты, добавленные в разметку, попадут в сгенерированный код. Однако стоит учесть, что нельзя одновременно задать атрибут action и один из атрибутов asp-controller / asp-action, в этом случае будет выброшено исключение: «Cannot override the «action» attribute for. A with a specified «action» must not have attributes starting with «asp-route-» or an «asp-action» or «asp-controller» attribute.»
Иногда действию контроллера нужно передать дополнительные параметры. Это можно сделать с помощью атрибутов, начинающихся с asp-route-. Например, в действие Login часто передается параметр ReturnUrl, вот как это можно сделать с использованием тег-хэлперов:
В итоге будет сгенерирован такой HTML код:
Используя такой синтаксис, можно указывать столько параметров, сколько необходимо.
Еще тег-хэлпер form поддерживает возможность указать именованный маршрут вместо пары контроллер / действие. Предположим, что мы определили маршрут с именем login в MVC коде:
routes.MapRoute(
name: "login",
template: "login",
defaults: new { controller = "Account", action = "Login" });
Тогда мы можем указать этот маршрут для формы, используя атрибут asp-route:
Что в свою очередь сгенерирует точно такую же HTML разметку, как и в предыдущем случае:
Input
Тег-хэлпер input является альтернативой Html.EditorFor. В следующем примере будем использовать такой простой класс модели представления:
public class SimpleViewModel
{
public string Email { get; set; }
}
Поле ввода для Email можно создать с помощью asp-for атрибута:
В результате чего получим следующую HTML разметку:
Тег-хэлпер добавляет атрибуты id и name, значение которых задается именем свойства, указанным в asp-for. Тип сгенерированного input был установлен в text потому, что тип свойства Email string. Если бы тип свойства был bool, то тег-хэлпер сгенерировал бы input с типом checkbox. Ниже представлена таблица соответствия .NET типов и HTML input типов.
.NET тип | HTML input тип |
String | type=«text» |
DateTime | type=«datetime» |
Byte | type=«number» |
Int16, Int32, Int64 | type=«number» |
Single, Double | type=«number» |
Boolean | type=«checkbox» |
Тег-хэлпер input принимает во внимание не только тип свойства, но и валидационные атрибуты. Например, добавим [Required] к свойству Email:
public class SimpleViewModel
{
[Required]
public string Email { get; set; }
}
В этом случае в сгенерированной HTML разметке будет атрибут data-val-required, который используется jQuery Validation.
Если добавить атрибут [EmailAddress] к свойству модели, то будет сгенерирован input с типом email и атрибутом data-val-email. Ниже приведена таблица с указанием .NET атрибутов, которые соответствуют различным типам HTML input.
Атрибут | HTML input тип |
[EmailAddress] | type=«email» |
[Url] | type=«url» |
[HiddenInput] | type=«hidden» |
[DataType (DataType.Password)] | type=«password» |
[DataType (DataType.Date)] | type=«date» |
[DataType (DataType.Time)] | type=«time» |
Также можно использовать дочерние объекты в моделях представления. Чуть усложним нашу модель.
public class AddressViewModel
{
public string AddressLine1 { get; set; }
}
public class RegisterViewModel
{
public string UserName { get; set;}
public AddressViewModel Address { get; set; }
}
В представлении мы можем создать поле ввода для Address.AddressLine1:
В результате получим следующий HTML код:
Конечно, все атрибуты, которые будут в cshtml файле применены тег-хэлперу input, будут добавлены в сгенерированную HTML разметку.
Еще одной возможностью, предоставляемой тег-хэлпером input, является использование обычной msdn.microsoft.com/en-us/library/dwhawy9k.aspx»>строки форматирования, широко использующейся в .NET. Для этого используется атрибут asp-format, например:
В этом случае число будет показано с 4 знаками после запятой, например, 1.2000. Никаких дополнительных атрибутов не добавляется.
Как мы уже говорили, Visual Studio предоставляет IntelliSense для тег-хэлпера input, подсвечивает ошибки, если есть ошибка в имени свойства. Исключение в таком случае будет выброшено во время компиляции, но по умолчанию Razor представления не прекомпилируются, поэтому эта ошибка будет видна только при первом обращении к странице.
TextArea
Тег-хэлпер textarea во многом похож на ранее рассмотренный input и является альтернативой Html.TextAreaFor. Он также использует атрибут asp-for. Рассмотрим следующую модель представления:
public class SimpleViewModel
{
[Required]
[MaxLength(5000)]
public string Description { get; set; }
}
Мы используем тег-хэлпер textarea следующим образом:
Что сгенерирует следующую HTML разметку:
В сгенерированном элементе textarea определены name, id, добавлены валидационные атрибуты.
Validation
Мы увидели, что добавлять валидационные атрибуты несложно, теперь обсудим, где показывать валидационные сообщения. Раньше для этого использовался метод Html.ValidationMessageFor, теперь — атрибут asp-validation-for, применяемый к элементу span.
Это сгенерирует следующую HTML разметку, в случае если поле Email было сделано обязательным, но не было заполнено:
The Email field is required.
Тег-хэлпер нашел валидационное сообщение для свойства Email и добавил его в содержимое элемента span. Также он добавил data-valmsg-* атрибуты, чтобы клиентская валидация jQuery для поля Email работала с этим элементом. Обычно тег-хэлпер валидации будет помещен на форму рядом с полем, которое он валидирует. Как и для всех остальных тег-хэлперов, все HTML атрибуты, которые вы к нему добавите, попадут в сгенерированную HTML разметку.
Теперь рассмотрим аналог Html.ValidationSummary (true) — тег-хэлпер Validation Summary, который агрегирует валидационные сообщения для валидационных сообщений уровня всей модели и/или уровня свойств модели. Этот тег-хэлпер представляет собой атрибут asp-validation-summary, который добавляется к элементу div. Возможные значения этого атрибута:
- ValidationSummary.All — будут показаны и сообщения уровня всей модели, и сообщения для всех ее свойств.
- ValidationSummary.ModelOnly — будут показаны только сообщения уровня всей модели
- ValidationSummary.None — тег-хэлпер ничего не делает, как если бы вы не добавляли атрибут asp-validation-summary. Единственным применением видится быстрое отключение всех валидационных сообщений для каких-то своих девелоперских нужд.
Этот код сгенерирует следующую разметку в случае, когда валидационных ошибок нет:
Если же у нас есть ошибки валидации:
- The Email field is required.
- The Password field is required.
Label
Тег-хэлпер label является самым скучным простым тег-хэлпером, заменой Html.LabelFor. Его единственная задача — сгенерировать элемент label для поля, имя которого указано в атрибуте asp-for. Значение для содержимого этого label задается свойством Name атрибута Description. Рассмотрим такую модель представления:
public class SimpleViewModel
{
[Display(Name="Email Address")]
public string Email { get; set; }
}
Теперь используем тег-хэлпер label:
Что в результате даст нам следующую HTML разметку:
Select
Тег-хэлпер select используется для создания выпадающих списков в HTML разметке вместо Html.DropDownListFor. Пусть у нас есть модель со свойством CountryCode, которое мы хотим заполнять с помощью выпадающего списка:
public class SimpleViewModel
{
public string CountryCode { get; set; }
}
Теперь с помощью тег-хэлпера свяжем это поле с элементом select.
И получим следующий HTML код:
Конечно, этот код не несет практической ценности, потому что выпадающий список еще пуст. Есть 2 способа его заполнить. Первый (для совсем маленьких списков): все возможные варианты задать вручную в представлении:
В этом случае значение будет выбрано исходя из значения свойства в модели. Например, если CountryCode равен «CA», будет сгенерирована следующая HTML разметка:
Второй способ задания возможных значений — динамическая загрузка из источника данных. Для этого нужен источник данных типа IEnumerable
Для того, чтобы сгенерированный элемент select поддерживать возможность выбора нескольких элементов, достаточно, чтобы свойство модели имело тип IEnumerable, например:
public class SimpleViewModel
{
public IEnumerable CountryCodes { get; set; }
}
В коде использования тег-хэлпера ничего не меняем:
Но сгенерированная HTML разметка будет другой:
Anchor
Тег-хэлпер anchor служит для генерации ссылок и является заменой как Html.ActionLink, так и Url.Action, с помощью которых мы обычно писали вот так:
@Html.ActionLink("Register", "Register", "Account")
Register
И в результате получали две одинаковые ссылки:
Register
Register
Теперь с помощью тег-хэлпера anchor и атрибутов asp-controller, asp-action ссылку можно добавить следующим образом:
Register
Как и в случае остальных тег-хэлперов, их использование позволяет проще добавить дополнительные классы и атрибуты для этих элементов, в отличие от Html.ActionLink («Register », «Create», «Account», null, new { class= «css-class»}).
Если в метод контроллера надо передать дополнительные параметры, то это можно сделать так же, как и для тег-хэлпера form: с помощью атрибутов asp-route-*, например:
View Details
Также можно указать именованный маршрут (опять точно так же, как для form):
Login
Ранее используемый Html.ActionLink позволял указать протокол, хост и фрагмент генерируемого URL. У тег-хэлпера есть для этого свои атрибуты:
Register
Register
Cache
У тег-хэлпера cache нет аналога среди Html хэлперов, да и обычным HTML элементом он не является. Он позволяет закешировать свое содержимое в памяти. Перед каждой обработкой своего содержимого он проверяет, не было ли оно уже сохранено в MemoryCache. Если содержимое найдено в кеше, оно отправляется сразу в Razor движок, если нет — сначала Razor обработает его, а потом оно сохранится в кеш. По умолчанию тег-хэлпер сгенерирует уникальный ID своего содержимого. Вот так его можно использовать в представлениях:
@Html.Partial("_WhatsNew")
*last updated @DateTime.Now.ToLongTimeString()
Тег cache не будет включен в сгенерированную HTML разметку, это просто директива для движка, что на протяжении 10 минут клиенту можно посылать уже обработанную и закешированную версию представления _WhatsNew.
Если не указать время жизни закешированной версии, то по умолчанию она будет отдаваться все время жизни приложения (или до первого ресайкла пула). Тег-хэлпер cache поддерживает следующие опции задания времени жизни закешированного объекта:
- expires-after: задает промежуток времени от текущего момента, во время которого кеш считается валидным, например, 5 секунд. Атрибут expires-after ожидает объект типа TimeSpan:
*last updated @DateTime.Now.ToLongTimeString() - expires-on: принимает объект типа DateTime, который показывает, в какой момент кеш перестанет быть валидным:
*last updated @DateTime.Now.ToLongTimeString() - expires-sliding: принимает объект типа TimeSpan, который задает промежуток времени, если в течение которого объект в кеше не запрашивался, то кеш перестает быть валидным.
*last updated @DateTime.Now.ToLongTimeString()
По умолчанию ключ объекта в кеше генерируется уникально на основе контекста тег-хэлпера, что позволяет использовать несколько тег-хэлперов cache на одной странице без путаницы их содержимого в кеше. С помощью атрибутов vary-by можно задавать более сложные ключи, позволяющие кешировать разное содержимое тег-хэлпера cache для разных запросов. Рассмотрим возможные варианты атрибутов vary-by.
- vary-by-user: принимает булевское значение, позволяет кешировать различное содержимое для различных залогиненных пользователей. К ключу объекта в кеше добавляется имя пользователя.
*last updated @DateTime.Now.ToLongTimeString() - vary-by-route: принимает список имен параметров маршрутизации, значения которых будут добавлены к ключу объекта. Например, так можно кешировать содержимое в зависимости от значения параметра id:
*last updated @DateTime.Now.ToLongTimeString() - vary-by-query: принимает список имен параметров запроса, значения которых будут добавлены к ключу объекта в кеше. Например, так можно кешировать в зависимости от значения параметра search:
*last updated @DateTime.Now.ToLongTimeString() - vary-by-header: принимает имя HTTP заголовка (одно, не список), значение которого будет добавлено к ключу. Вот так можно кешировать содержимое в зависимости от заголовка User-Agent:
*last updated @DateTime.Now.ToLongTimeString() - vary-by: позволяет кешировать различное содержимое тег-хэлпера в зависимости от значения произвольной строки, если ни один из рассмотренных выше атрибутов vary-by-* не подходит. Например, можно добавить свойство ProductId, хранящееся во ViewBag:
*last updated @DateTime.Now.ToLongTimeString() - Составные ключи: можно применять сразу несколько различных атрибутов vary-by, например и по пользователю, и по параметру маршрутизации id:
*last updated @DateTime.Now.ToLongTimeString()
Так как все закешированное содержимое хранится в IMemoryCache, то его размер ограничен размером доступной оперативной памяти. Поэтому если процесс начинает испытывать нехватку памяти, кеш будет удалять свои элементы для освобождения памяти. С помощью атрибута priority можно указать, какие объекты следует удалять первыми, а какие оставлять в кеше. Возможные значения этого атрибута: Low, Normal, High и NeverRemove. Например, укажем, что содержимое тег-хэлпера можно считать неприоритетным:
@using Microsoft.Framework.Caching.Memory
*last updated @DateTime.Now.ToLongTimeString()
Ограничения этого тег-хэлпера связаны с тем, что он использует MemoryCache в свое реализации. Первое ограничение связано с тем, что любой перезапуск процесса, ресайкл пула приложения и похожее действие инвалидирует весь наш кеш, в облаке такое может произойти, если сайт переедет на новую машину. Поэтому нельзя считать этот кеш надежным. Второе ограничение также связано с тем, что кеш хранится в памяти. Если пользователь в рамках сессии будет перенаправлен на разные веб-сервера, потенциально он может увидеть разное содержимое одного и того же элемента. Это связано с тем, что у каждого сервера будет своя версия кеша, реализация MemoryCache не является распределенной. В данном случае может помочь привязка пользователя к одному и тому же серверу (sticky session, server affinity).
Image
Этот простой в использовании тег-хэлпер состоит из всего одного атрибута asp-append-version, добавляемого к тегу img. Если передать этому атрибуту значение true, то к URL изображения будет добавлена строка, зависящая от содержимого картинки (как и в случае css / js файлов), что позволит браузеру кешировать ее неопределенно долгое время.
В результате будет сгенерирована похожая HTML разметка:
Использование этого тег-хэлпера упрощает выбор политики кеширования изображений сайта: при маленьком времени кеширования нивелируется выгода от его применения, при большом — потенциально при обновлении изображения клиент в течение долгого времени продолжает использовать старую локально закешированную версию изображения.
Заключение
С одной стороны плохо, что все наши представления с версией MVC
Ссылки
Документация по тег-хэлперам
Документация по последней версии ASP.NET MVC
Visual Studio 2015 Community Edition