[Перевод] Полное руководство по использованию 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).
Рисунок 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).
Рисунок 2

Для того, чтобы протестировать среды Staging или Production, нужно создать новый профиль отладки (Debug Profile) и установить требуемое значение Hosting: Environment. Например, рассмотрим, как создать профиль Production (рисунок 3).

  • Кликнуть правой кнопкой мыши по проекту в обозревателе решений и выбрать Свойства.
  • Выбрать вкладку Debug (Отладка).
  • Нажмите New… для создания нового профиля и назовите его «IIS Express — Prod».
  • В выпадающем списке Launch выберите «IIS Express».
  • Добавьте новую переменную окружения с именем «Hosting: Environment» и значением «Production».
  • Сохраните проект.


Рисунок 3

Теперь в выпадающем списке Run в Visual Studio можно выбрать профиль «IIS Express — Prod» (рисунок 4).
Рисунок 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" } })




В данном случае код с использованием тег-хэлперов однозначно выигрывает и в красоте, и в простоте.
Теперь приведем код всей формы логина в двух вариантах.

Код формы с использование HTML Helpers
@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):
Рисунок 5

Внутри атрибутов asp-for работает IntelliSense, подсказывающий имена свойств модели (рисунок 6).
image

Теперь рассмотрим атрибуты тег-хэлпера 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 (или SelectList), пусть в нашем случае он доступен в ViewBag.Countries. Для привязки источника данных используется атрибут asp-items:




Для того, чтобы сгенерированный элемент 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 поддерживает следующие опции задания времени жизни закешированного объекта:

  1. expires-after: задает промежуток времени от текущего момента, во время которого кеш считается валидным, например, 5 секунд. Атрибут expires-after ожидает объект типа TimeSpan:
    
        
        *last updated  @DateTime.Now.ToLongTimeString()
    
    
    

  2. expires-on: принимает объект типа DateTime, который показывает, в какой момент кеш перестанет быть валидным:
    
      
     *last updated  @DateTime.Now.ToLongTimeString()
    
    
    

  3. expires-sliding: принимает объект типа TimeSpan, который задает промежуток времени, если в течение которого объект в кеше не запрашивался, то кеш перестает быть валидным.
    
        
        *last updated  @DateTime.Now.ToLongTimeString()
    
    
    


По умолчанию ключ объекта в кеше генерируется уникально на основе контекста тег-хэлпера, что позволяет использовать несколько тег-хэлперов cache на одной странице без путаницы их содержимого в кеше. С помощью атрибутов vary-by можно задавать более сложные ключи, позволяющие кешировать разное содержимое тег-хэлпера cache для разных запросов. Рассмотрим возможные варианты атрибутов vary-by.

  1. vary-by-user: принимает булевское значение, позволяет кешировать различное содержимое для различных залогиненных пользователей. К ключу объекта в кеше добавляется имя пользователя.
     
         
        *last updated @DateTime.Now.ToLongTimeString() 
    
    
    

  2. vary-by-route: принимает список имен параметров маршрутизации, значения которых будут добавлены к ключу объекта. Например, так можно кешировать содержимое в зависимости от значения параметра id:
    
        
        *last updated  @DateTime.Now.ToLongTimeString()
    
    
    

  3. vary-by-query: принимает список имен параметров запроса, значения которых будут добавлены к ключу объекта в кеше. Например, так можно кешировать в зависимости от значения параметра search:
    
        
        *last updated  @DateTime.Now.ToLongTimeString()
    
    
    

  4. vary-by-header: принимает имя HTTP заголовка (одно, не список), значение которого будет добавлено к ключу. Вот так можно кешировать содержимое в зависимости от заголовка User-Agent:
    
        
        *last updated  @DateTime.Now.ToLongTimeString()
    
    
    

  5. vary-by: позволяет кешировать различное содержимое тег-хэлпера в зависимости от значения произвольной строки, если ни один из рассмотренных выше атрибутов vary-by-* не подходит. Например, можно добавить свойство ProductId, хранящееся во ViewBag:
    
        
        *last updated  @DateTime.Now.ToLongTimeString()
    
    
    

  6. Составные ключи: можно применять сразу несколько различных атрибутов 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 файлов), что позволит браузеру кешировать ее неопределенно долгое время.

company logo


В результате будет сгенерирована похожая HTML разметка:

company logo


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

Заключение


С одной стороны плохо, что все наши представления с версией MVC

Ссылки


Документация по тег-хэлперам
Документация по последней версии ASP.NET MVC
Visual Studio 2015 Community Edition

© Habrahabr.ru