Pull to refresh

Полное руководство по использованию ASP.NET Core 1.0 (aka ASP.NET 5) Tag Helpers

Reading time22 min
Views36K
Original author: David Paquette
Тег-хэлперы (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, добавив все файлы по шаблону:
<script asp-src-include="~/app/**/*.js"></script>

В результате будет сгенерирована следующая HTML разметка:
<script src="/app/app.js"></script>
<script src="/app/controllers/controller1.js"></script>
<script src="/app/controllers/controller2.js"></script>
<script src="/app/controllers/controller3.js"></script>
<script src="/app/controllers/controller4.js"></script>
<script src="/app/services/service1.js"></script>
<script src="/app/services/service2.js"></script>

В данном примере мы использовали шаблон /app/**/*.js, потому что нам нужно было включить еще и js файлы из подпапок. Если же мы хотим добавить ссылки на все js файлы из одной папки, то используем шаблон /app/*.js.
Наряду с атрибутом asp-src-include существует и атрибут asp-src-exclude. Например, в предыдущем примере мы можем сделать так, чтобы ссылки на js файлы в подпапке services не были добавлены:
<script asp-src-include="~/app/**/*.js" asp-src-exclude="~/app/services/**/*.js" >
</script>

Тег-хэлпер Link работает точно так же, только с атрибутами asp-href-include и asp-href-exclude.
Теперь рассмотрим атрибуты этих тег-хэлперов, которые помогут подключать CDN. Преимущества использования CDN для хостинга файлов таких популярных фреймворков как jQuery и Bootstrap очевидны: уменьшается нагрузка на наши сервера и потенциально увеличивается производительность у клиента (например, актуальная версия файла уже может быть в кеше браузера у клиента). Среди минусов – нужно предусмотреть возможность того, что CDN не отвечает и нам нужно отдавать файлы со своего сервера. С помощью тег-хэлперов это сделать проще:
<link rel="stylesheet" href="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/css/bootstrap.min.css" 
      asp-fallback-href="~/lib/bootstrap/css/bootstrap.min.css"
      asp-fallback-test-class="hidden" 
      asp-fallback-test-property="visibility" 
      asp-fallback-test-value="hidden" />
<script src="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/bootstrap.min.js"
        asp-fallback-src="~/lib/bootstrap/js/bootstrap.min.js"
        asp-fallback-test="window.jQuery">
</script>

Атрибуты asp-fallback-test позволяют указать свойства или объекты, которые нужно проверить для того, чтобы узнать, был ли загружен файл или нет. Если нет, то файл будет загружен с использованием пути, заданном в атрибуте asp-fallback-href. Сгенерированная HTML разметка:
<link rel="stylesheet" href="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/css/bootstrap.min.css" />
<meta name="x-stylesheet-fallback-test" class="hidden" />
<script>!function(a,b,c){var d,e=document,f=e.getElementsByTagName("SCRIPT"),g=f[f.length-1].previousElementSibling,h=e.defaultView&&e.defaultView.getComputedStyle?e.defaultView.getComputedStyle(g):g.currentStyle;if(h&&h[a]!==b)for(d=0;d<c.length;d++)e.write('<link rel="stylesheet" href="'+c[d]+'"/>')}("visibility","hidden",["\/lib\/bootstrap\/css\/bootstrap.min.css"]);</script>

<script src="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/bootstrap.min.js">
</script>
<script>(typeof($.fn.modal) === 'undefined'||document.write("<script src=\"\/lib\/bootstrap\/js\/bootstrap.min.js\"><\/script>"));</script>

Cache busting (инвалидация кеша) – механизм, добавляющий идентификатор версии файла (на основе его содержимого) к имени файла для css и js файлов. В этом случае можно указать браузеру, что эти файлы можно кешировать на неопределенно долгое время, в случае их изменения имя у них также изменится. Для того чтобы включить этот механизм, достаточно добавить атрибут asp-append-version:
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true"/>

Сгенерированный HTML:
<link rel="stylesheet" href="/css/site.min.css?v=UdxKHVNJA5vb1EsG9O9uURFDfEE3j1E3DgwL6NiDGMc" />


Environment


Тег-хэлпер Environment используется (обычно вместе с тег-хэлперами Link и Script) для генерирования различной HTML разметки в зависимости от среды: девелопмент vs тест vs продакшен. Этот тег-хэлпер используется следующим образом: оберните в этот тег часть HTML кода и укажите среду или среды, для которых эта часть разметки должна быть включена в выходной файл. В следующем примере в девелопмент среде мы подключаем все css файлы по отдельности, а в средах Staging и Production используем скомбинированную минифицированную версию.
<environment names="Development">            
    <link rel="stylesheet" href="~/css/site1.css" />
    <link rel="stylesheet" href="~/css/site2.css" />
</environment>
<environment names="Staging,Production">
    <link rel="stylesheet" href="~/css/site.min.css" asp-file-version="true"/>
</environment>

В девелопмент среде будет сгенерирован следующий HTML код:
<link rel="stylesheet" href="/css/site1.css" />
<link rel="stylesheet" href="/css/site2.css" />

В средах Staging и Production будет добавлена ссылка на минифицированную версию:
<link rel="stylesheet" href="/css/site.min.css?v=UdxKHVNJA5vb1EsG9O9uURFDfEE3j1E3DgwL6NiDGMc"/>

Сам тег 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 Helper (MVC 5 и ниже)-->
@Html.EditorFor(l => l.UserName)
<!--С помощью Tag Helper (MVC 6)-->
<input asp-for="UserName" />

Когда мы используем HTML хэлпер, то вызываем обычный C# метод, который возвращает некоторую HTML разметку. С тег-хэлперами мы имеем дело сразу с обычной HTML разметкой, в которой встречаются MVC специфичные атрибуты. В результате обоих подходов на выходе мы получим один и тот же сгенерированный HTML код:
<input name="UserName" class="text-box single-line" id="UserName" type="text" value="">

Почему подход с тег-хэлперами лучше? Код с ними становится читаемее и проще. Например, мы хотим добавить к полю ввода класс:
<!--С помощью Html Helper (MVC 5 и ниже)-->
@Html.EditorFor(l => l.UserName, new { htmlAttributes = new { @class = "form-control" } })
<!--С помощью Tag Helper (MVC 6)-->
<input asp-for="UserName" 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" })

    <div class="form-group">
        <div class="row">
            @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
                @Html.ValidationMessageFor(m => m.UserName, "", new { @class = "text-danger" })
            </div>
        </div>
    </div>
    <div class="form-group">
        <div class="row">
            @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
                @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
            </div>
        </div>
    </div>
    <div class="form-group">
        <div class="row">
            <div class="col-md-offset-2 col-md-2">
                <input type="submit" value="Log in" class="btn btn-primary" />
            </div>
        </div>
    </div>
}


Та же самая форма с использованием тег-хэлперов
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewBag.ReturnUrl" method="post" class="form-horizontal" role="form">
    <div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
    <div class="form-group">
        <label asp-for="UserName" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="UserName" class="form-control" />
            <span asp-validation-for="UserName" class="text-danger"></span>
        </div>
    </div>
    <div class="form-group">
        <label asp-for="Password" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="Password" class="form-control" />
            <span asp-validation-for="Password" class="text-danger"></span>
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Log in" class="btn btn-default" />
        </div>
    </div>
</form>


Как мы видим, версия с тег-хэлперами выглядит проще и читаемее. Особенно мне нравится отсутствие выражения using для генерации формы, мне всегда это казалось каким-то хаком. Еще стоит отметить, что нам теперь не нужно явно добавлять AntiForgeryToken на форму. Тег-хэлпер form делает это автоматически, если не отключить это поведение атрибутом asp-anti-forgery=”false”.
Конечно, Visual Studio подсвечивает атрибуты тег-хэлперы, чтобы их легко можно было отличить от стандартных HTML атрибутов (рисунок 5):
Рисунок 5

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

Теперь рассмотрим атрибуты тег-хэлпера form. Например для того, чтобы связать форму с определенным действием определенного контроллера, используются атрибуты asp-controller и asp-action:
<form asp-controller="Account" 
      asp-action="Login">
//Элементы формы
</form>

В результате будет сгенерирована следующая HTML разметка:
<form action="/Account/Login" method="post">
//Элементы формы
    <input name="__RequestVerificationToken" type="hidden" value="CfDJ8AFtmUdx-b5MkQvAyGYbjFmMGSMv0Fmk7gG4RqGXlkNV6yqKqj6fgqnOh4TLT6ZnWSaqtAbKkgpEB20lvfkc2iOKZKIqt3tJ4Jij8DjmatTrZo-DKVOLwwOzj3kB8VKpFwc0rQMjaJTTC_gVv5f0vAg">
</form>

Как мы видим, по умолчанию добавляется 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, вот как это можно сделать с использованием тег-хэлперов:
<form asp-controller="Account" 
      asp-action="Login" 
      asp-route-returnurl="@ViewBag.ReturnUrl" 
      method="post" >
</form>

В итоге будет сгенерирован такой HTML код:
<form action="/Account/Login?returnurl=%2FHome%2FAbout" method="post">
    <input name="__RequestVerificationToken" type="hidden" value="CfDJ8AFtmUdx-b5MkQvAyGYbjFmMGSMv0Fmk7gG4RqGXlkNV6yqKqj6fgqnOh4TLT6ZnWSaqtAbKkgpEB20lvfkc2iOKZKIqt3tJ4Jij8DjmatTrZo-DKVOLwwOzj3kB8VKpFwc0rQMjaJTTC_gVv5f0vAg">
</form>

Используя такой синтаксис, можно указывать столько параметров, сколько необходимо.
Еще тег-хэлпер form поддерживает возможность указать именованный маршрут вместо пары контроллер / действие. Предположим, что мы определили маршрут с именем login в MVC коде:
routes.MapRoute(
    name: "login",
    template: "login",
    defaults: new { controller = "Account", action = "Login" });

Тогда мы можем указать этот маршрут для формы, используя атрибут asp-route:
<form asp-route="login" 
      asp-route-returnurl="@ViewBag.ReturnUrl" 
      method="post" >
</form>

Что в свою очередь сгенерирует точно такую же HTML разметку, как и в предыдущем случае:
<form action="/login?returnurl=%2FHome%2FAbout" method="post">
    <input name="__RequestVerificationToken" type="hidden" value="CfDJ8AFtmUdx-b5MkQvAyGYbjFmMGSMv0Fmk7gG4RqGXlkNV6yqKqj6fgqnOh4TLT6ZnWSaqtAbKkgpEB20lvfkc2iOKZKIqt3tJ4Jij8DjmatTrZo-DKVOLwwOzj3kB8VKpFwc0rQMjaJTTC_gVv5f0vAg">
</form>


Input


Тег-хэлпер input является альтернативой Html.EditorFor. В следующем примере будем использовать такой простой класс модели представления:
public class SimpleViewModel
{
   public string Email { get; set; }
}

Поле ввода для Email можно создать с помощью asp-for атрибута:
<input asp-for="Email" />

В результате чего получим следующую HTML разметку:
<input type="text" id="Email" name="Email" value="thisaddress@isfrommymodel.com" />

Тег-хэлпер добавляет атрибуты 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.
<input type="text" data-val="true" 
                   data-val-required="The Email field is required." 
                   id="Email" 
                   name="Email" 
                   value="" />

Если добавить атрибут [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:
<input asp-for="Address.AddressLine1" />

В результате получим следующий HTML код:
<input name="Address.AddressLine1" id="Address_AddressLine1" type="text" value="">

Конечно, все атрибуты, которые будут в cshtml файле применены тег-хэлперу input, будут добавлены в сгенерированную HTML разметку.
Еще одной возможностью, предоставляемой тег-хэлпером input, является использование обычной <a href=” msdn.microsoft.com/en-us/library/dwhawy9k.aspx”>строки форматирования, широко использующейся в .NET. Для этого используется атрибут asp-format, например:
<input asp-for="SomeNumber" asp-format="{0:N4}"/>

В этом случае число будет показано с 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 следующим образом:
<textarea asp-for="Description"></textarea>

Что сгенерирует следующую HTML разметку:
<textarea name="Description" id="Description" 
          data-val-required="The Description field is required." 
          data-val-maxlength-max="5000" 
          data-val-maxlength="The field Description must be a string or array type with a maximum length of '5000'." 
          data-val="true"></textarea>

В сгенерированном элементе textarea определены name, id, добавлены валидационные атрибуты.

Validation


Мы увидели, что добавлять валидационные атрибуты несложно, теперь обсудим, где показывать валидационные сообщения. Раньше для этого использовался метод Html.ValidationMessageFor, теперь – атрибут asp-validation-for, применяемый к элементу span.
<span asp-validation-for="Email"></span>

Это сгенерирует следующую HTML разметку, в случае если поле Email было сделано обязательным, но не было заполнено:
<span class="field-validation-error" 
         data-valmsg-replace="true" 
         data-valmsg-for="Email"> 
   The Email field is required.</span>

Тег-хэлпер нашел валидационное сообщение для свойства 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. Единственным применением видится быстрое отключение всех валидационных сообщений для каких-то своих девелоперских нужд.

<div asp-validation-summary="ValidationSummary.All"></div>

Этот код сгенерирует следующую разметку в случае, когда валидационных ошибок нет:
<div class="validation-summary-valid" data-valmsg-summary="true">
  <ul>
    <li style="display: none;"></li>
  </ul>
</div>

Если же у нас есть ошибки валидации:
<div class="validation-summary-errors" data-valmsg-summary="true">
  <ul>
    <li>The Email field is required.</li>
    <li>The Password field is required.</li>
  </ul>
</div>


Label


Тег-хэлпер label является самым скучным простым тег-хэлпером, заменой Html.LabelFor. Его единственная задача – сгенерировать элемент label для поля, имя которого указано в атрибуте asp-for. Значение для содержимого этого label задается свойством Name атрибута Description. Рассмотрим такую модель представления:
public class SimpleViewModel
{
    [Display(Name="Email Address")]
    public string Email { get; set; }
}

Теперь используем тег-хэлпер label:
<label asp-for="Email"></label>

Что в результате даст нам следующую HTML разметку:
<label for="Email">Email Address</label>


Select


Тег-хэлпер select используется для создания выпадающих списков в HTML разметке вместо Html.DropDownListFor. Пусть у нас есть модель со свойством CountryCode, которое мы хотим заполнять с помощью выпадающего списка:
public class SimpleViewModel
{
    public string CountryCode { get; set; }
}

Теперь с помощью тег-хэлпера свяжем это поле с элементом select.
<select asp-for="CountryCode"></select>

И получим следующий HTML код:
<select name="CountryCode" id="CountryCode">
</select>

Конечно, этот код не несет практической ценности, потому что выпадающий список еще пуст. Есть 2 способа его заполнить. Первый (для совсем маленьких списков): все возможные варианты задать вручную в представлении:
<select asp-for="CountryCode">
    <option value="CA">Canada</option>
    <option value="US">US</option>
    <option value="--">Other</option>
</select>

В этом случае значение будет выбрано исходя из значения свойства в модели. Например, если CountryCode равен “CA”, будет сгенерирована следующая HTML разметка:
<select name="CountryCode" id="CountryCode">
    <option selected="selected" value="CA">Canada</option>
    <option value="US">US</option>
    <option value="--">Other</option>
</select>

Второй способ задания возможных значений – динамическая загрузка из источника данных. Для этого нужен источник данных типа IEnumerable<SelectListItem> (или SelectList), пусть в нашем случае он доступен в ViewBag.Countries. Для привязки источника данных используется атрибут asp-items:
<select asp-for="CountryCode" 
         asp-items="ViewBag.Countries">
</select>

Для того, чтобы сгенерированный элемент select поддерживать возможность выбора нескольких элементов, достаточно, чтобы свойство модели имело тип IEnumerable, например:
public class SimpleViewModel
{
    public IEnumerable<string> CountryCodes { get; set; }
}

В коде использования тег-хэлпера ничего не меняем:
<select asp-for="CountryCodes" 
        asp-items="ViewBag.Countries">
</select>

Но сгенерированная HTML разметка будет другой:
<select name="CountryCodes" 
        id="CountryCodes" 
        multiple="multiple">
    <option selected="selected" 
            value="CA">Canada</option>
    <option value="USA">United States</option>
    <option value="--">Other</option>
</select>

Anchor


Тег-хэлпер anchor служит для генерации ссылок и является заменой как Html.ActionLink, так и Url.Action, с помощью которых мы обычно писали вот так:
@Html.ActionLink("Register", "Register", "Account")

<a href="@Url.Action("Register", "Account")">Register</a>

И в результате получали две одинаковые ссылки:
<a href="/Account/Register">Register</a>
<a href="/Account/Register">Register</a>

Теперь с помощью тег-хэлпера anchor и атрибутов asp-controller, asp-action ссылку можно добавить следующим образом:
<a asp-controller="Account" 
    asp-action="Register">Register</a>

Как и в случае остальных тег-хэлперов, их использование позволяет проще добавить дополнительные классы и атрибуты для этих элементов, в отличие от Html.ActionLink(«Register », «Create», “Account”, null, new { class= «css-class»} ).
Если в метод контроллера надо передать дополнительные параметры, то это можно сделать так же, как и для тег-хэлпера form: с помощью атрибутов asp-route-*, например:
<a asp-controller="Product" 
   asp-action="Display" 
   asp-route-id="@ViewBag.ProductId">
View Details</a>

Также можно указать именованный маршрут (опять точно так же, как для form):
<a asp-route="login">Login</a>

Ранее используемый Html.ActionLink позволял указать протокол, хост и фрагмент генерируемого URL. У тег-хэлпера есть для этого свои атрибуты:
<a asp-controller="Account" 
   asp-action="Register" 
   asp-protocol="https" 
   asp-host="asepecificdomain.com" 
   asp-fragment="fragment">Register</a>

<!-- или только указывая протокол -->
<a asp-controller="Account" 
   asp-action="Register" 
   asp-protocol="https">Register</a>


Cache


У тег-хэлпера cache нет аналога среди Html хэлперов, да и обычным HTML элементом он не является. Он позволяет закешировать свое содержимое в памяти. Перед каждой обработкой своего содержимого он проверяет, не было ли оно уже сохранено в MemoryCache. Если содержимое найдено в кеше, оно отправляется сразу в Razor движок, если нет – сначала Razor обработает его, а потом оно сохранится в кеш. По умолчанию тег-хэлпер сгенерирует уникальный ID своего содержимого. Вот так его можно использовать в представлениях:
<cache expires-after="@TimeSpan.FromMinutes(10)">
    @Html.Partial("_WhatsNew")
    *last updated  @DateTime.Now.ToLongTimeString()
</cache>

Тег cache не будет включен в сгенерированную HTML разметку, это просто директива для движка, что на протяжении 10 минут клиенту можно посылать уже обработанную и закешированную версию представления _WhatsNew.
Если не указать время жизни закешированной версии, то по умолчанию она будет отдаваться все время жизни приложения (или до первого ресайкла пула). Тег-хэлпер cache поддерживает следующие опции задания времени жизни закешированного объекта:
  1. expires-after: задает промежуток времени от текущего момента, во время которого кеш считается валидным, например, 5 секунд. Атрибут expires-after ожидает объект типа TimeSpan:
    <cache expires-after="@TimeSpan.FromSeconds(5)">
        <!-- содержимое -->
        *last updated  @DateTime.Now.ToLongTimeString()
    </cache>
    

  2. expires-on: принимает объект типа DateTime, который показывает, в какой момент кеш перестанет быть валидным:
    <cache expires-on="@DateTime.Today.AddDays(1).AddTicks(-1)">
      <!--содержимое-->
     *last updated  @DateTime.Now.ToLongTimeString()
    </cache>
    

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


По умолчанию ключ объекта в кеше генерируется уникально на основе контекста тег-хэлпера, что позволяет использовать несколько тег-хэлперов cache на одной странице без путаницы их содержимого в кеше. С помощью атрибутов vary-by можно задавать более сложные ключи, позволяющие кешировать разное содержимое тег-хэлпера cache для разных запросов. Рассмотрим возможные варианты атрибутов vary-by.
  1. vary-by-user: принимает булевское значение, позволяет кешировать различное содержимое для различных залогиненных пользователей. К ключу объекта в кеше добавляется имя пользователя.
    <cache vary-by-user="true"> 
        <!-- содержимое --> 
        *last updated @DateTime.Now.ToLongTimeString() 
    </cache>
    

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

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

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

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

  6. Составные ключи: можно применять сразу несколько различных атрибутов vary-by, например и по пользователю, и по параметру маршрутизации id:
    <cache vary-by-user="true" vary-by-route="id">
        <!-- содержимое -->
        *last updated  @DateTime.Now.ToLongTimeString()
    </cache>
    


Так как все закешированное содержимое хранится в IMemoryCache, то его размер ограничен размером доступной оперативной памяти. Поэтому если процесс начинает испытывать нехватку памяти, кеш будет удалять свои элементы для освобождения памяти. С помощью атрибута priority можно указать, какие объекты следует удалять первыми, а какие оставлять в кеше. Возможные значения этого атрибута: Low, Normal, High и NeverRemove. Например, укажем, что содержимое тег-хэлпера можно считать неприоритетным:
@using Microsoft.Framework.Caching.Memory
<cache vary-by-user="true" 
       priority="@CachePreservationPriority.Low">
    <!-- содержимое -->
    *last updated  @DateTime.Now.ToLongTimeString()
</cache>

Ограничения этого тег-хэлпера связаны с тем, что он использует MemoryCache в свое реализации. Первое ограничение связано с тем, что любой перезапуск процесса, ресайкл пула приложения и похожее действие инвалидирует весь наш кеш, в облаке такое может произойти, если сайт переедет на новую машину. Поэтому нельзя считать этот кеш надежным. Второе ограничение также связано с тем, что кеш хранится в памяти. Если пользователь в рамках сессии будет перенаправлен на разные веб-сервера, потенциально он может увидеть разное содержимое одного и того же элемента. Это связано с тем, что у каждого сервера будет своя версия кеша, реализация MemoryCache не является распределенной. В данном случае может помочь привязка пользователя к одному и тому же серверу (sticky session, server affinity).

Image


Этот простой в использовании тег-хэлпер состоит из всего одного атрибута asp-append-version, добавляемого к тегу img. Если передать этому атрибуту значение true, то к URL изображения будет добавлена строка, зависящая от содержимого картинки (как и в случае css / js файлов), что позволит браузеру кешировать ее неопределенно долгое время.
<img src="~/images/logo.png" 
     alt="company logo" 
     asp-append-version="true" />

В результате будет сгенерирована похожая HTML разметка:
<img src="/images/logo.png?v=W2F5D366_nQ2fQqUk3URdgWy2ZekXjHzHJaY5yaiOOk" 
     alt="company logo"/>

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

Заключение


С одной стороны плохо, что все наши представления с версией MVC <= 5 надо переписывать, но с использованием тег-хэлперов они выглядят чище, их легче использовать дизайнерам / верстальщикам, теперь они выглядят как обычный HTML с дополнительными атрибутами. Следующий раз рассмотрим создание своего тег-хэлпера, а пока качайте VS 2015 Community Edition, создавайте свой первый проект ASP.NET Core 1.0!

Ссылки


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

Articles

Change theme settings