ASP.NET MVC 4
Адам Фриман
Работа с движком представления Razor
В предыдущем разделе мы смогли создать пользовательский движок представлений, реализовав всего два интерфейса. Следует признать, что он получился довольно незамысловатым и генерирует упрощенные представления, но прекрасно иллюстрирует концепцию расширяемости MVC на примере конвейера обработки запросов.
Всю сложность движку представлений добавляет система шаблонов представлений, которые включают в себя фрагменты кода и поддержку макетов и служат для оптимизации производительности. Мы не включали такие компоненты в наш пользовательский движок – они там и не нужны, так как все необходимое включено в движок Razor. В Razor доступна функциональность, которая требуется практически во всех приложениях MVC, и создавать пользовательский движок понадобится только в очень редких случаях.
Напомним, что Razor появился в MVC 3 и заменил предыдущий движок представлений (известный как ASPX или движок Web Forms). Вы можете использовать ASPX, но мы рекомендуем остановится на Razor, так как Microsoft в данный момент работает именно с ним. Основы синтаксиса Razor были рассмотрены в главе 5. В этой главе мы продемонстрируем вам другие функции для создания и отображения представлений Razor, а также разберем его настройки.
Создаем пример проекта
Для этой части главы мы создали проект WorkingWithRazor
с помощью шаблона Basic
. В нем был создан контроллер Home
, который показан в листинге 18-9.
Листинг 18-9: КонтроллерHome
в проектеWorkingWithRazor
using System.Web.Mvc;
namespace WorkingWithRazor.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
string[] names = {"Apple", "Orange", "Pear"};
return View(names);
}
}
}
Для этого контроллера мы создали представление Index.cshtml
, которое поместили в папку /Views/Home
. Содержимое файла представления показано в листинге 18-10.
Листинг 18-10: Содержимое файла View.cshtml
@model string[]
@{
ViewBag.Title = "Index";
}
This is a list of fruit names:
@foreach (string name in Model)
{
<span><b>@name</b></span>
}
Основы визуализации представлений в Razor
Движок Razor компилирует представления в приложении для повышения производительности. Представления преобразуются в классы C#, а затем компилируются - именно поэтому в них можно легко включать фрагменты кода C#. Полезно просматривать исходный код, генерируемый Razor, потому что это позволит проанализировать работу некоторых его функций.
Представления в приложении MVC не компилируются до запуска приложения, поэтому, чтобы увидеть классы, которые создаются Razor, необходимо запустить приложение и перейти по ссылке /Home/Index
. Первый запрос к приложению MVC запускает процесс компиляции для всех представлений. Вывод для нашего запроса показан на рисунке 18-4.
Рисунок 18-4: Вывод метода действияIndex
контроллераHome
Для удобства классы, генерируемые из файлов представлений, записываются на диск в виде файлов кода C#, а затем компилируются, что значит, что вы можете увидеть операторы C# для представлений. В Windows 7 и 8 генерируемые файлы можно найти по адресу c:\Users\<yourLoginName>\AppData\Local\Temp\Temporary ASP.NET Files
.
Найти код для конкретного представления будет немного сложнее. Как правило, здесь вы найдете несколько папок с загадочными именами, причем названия файлов .cs
не будут соответствовать именам классов, которые в них содержатся. Например, мы обнаружили класс, созданный для представления из листинга 18-10, в файле App_Web_cuymyfa4.0.cs
в папке root\bdd11980\ec057b05
. Мы его отредактировали и сделали более удобным для чтения, и вы можете увидеть его в листинге 18-11.
Листинг 18-11: Класс C#, сгенерированный для представления Razor
namespace ASP
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Helpers;
using System.Web.Security;
using System.Web.UI;
using System.Web.WebPages;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.Web.Mvc.Html;
using System.Web.Optimization;
using System.Web.Routing;
public class _Page_Views_Home_Index_cshtml : System.Web.Mvc.WebViewPage<string[]>
{
public _Page_Views_Home_Index_cshtml()
{
}
public override void Execute()
{
ViewBag.Title = "Index";
WriteLiteral("\r\n\r\nThis is a list of fruit names:\r\n\r\n");
foreach (string name in Model)
{
WriteLiteral(" <span><b>");
Write(name);
WriteLiteral("</b></span>\r\n");
}
}
}
}
Во-первых, отметим, что класс наследует от WebViewPage<T>
, где Т
– это тип модели: в нашем примереWebViewPage<string[]>
. Так обрабатываются сильно типизированные представления. Также обратите внимание на имя класса: _Page_Views_Home_Index_cshtml
. Как видите, здесь закодирован путь к файлу представления. Таким образом Razor соотносит запросы к представлениям с экземплярами скомпилированных классов.
В методе Execute
вы можете увидеть, как были обработаны операторы и элементы представления.
Фрагменты кода, которые мы отметили префиксом @
, записываются без изменений как операторы C#. Элементы HTML обрабатываются методом WriteLiteral
, который записывает содержимое параметра в результат. В отличие от него, метод Write
используется для переменных C# и кодирует значения строк, чтобы сделать их безопасными для использования в HTML-страницах.
Оба метода - и Write
, и WriteLiteral
- записывают содержимое в объект TextWriter
. Это тот же объект, который передается в метод IView.Render
, с которым мы работали в начале этой главы. Цель скомпилированного представления Razor - сгенерировать статический и динамический контент и отправить его клиенту с помощью TextWriter
. Имейте это в виду, когда мы будем рассматривать вспомогательные методы HTML далее в этой главе.
Настраиваем адреса поиска представлений
При поиске представлений движок Razor следует соглашению, установленному в более ранних версиях MVC Framework. Например, если вы запрашиваете представление Index
, связанное с контроллером Home
, Razor просмотрит следующий список представлений:
~ / Views / Home / Index.cshtml
;~ / Views / Home / Index.vbhtml
;~ / Views / Shared / Index.cshtml
;~ / Views / Shared / Index.vbhtml
.
Как вы теперь знаете, Razor на самом деле не ищет файлы на диске, потому что они уже скомпилированны в классы C#. Razor ищет скомпилированный класс, который содержит код данных представлений. Файлы .сshtml
представляют собой шаблоны, содержащие операторы C#, а файлы .vbhtml
содержат операторы Visual Basic.
Чтобы изменить файлы представлений, которые ищет Razor, нужно создать подкласс RazorViewEngine
. Он является реализацией IViewEngine
для Razor. Он наследует ряд базовых классов, которые определяют набор свойств, указывающих, какие файлы представлений нужно искать. Эти свойства описаны в таблице 18-1.
Таблица 18-1: Свойства поиска движка Razor
Свойство | Описание | Значение по умолчанию |
ViewLocationFormats
MasterLocationFormats
PartialViewLocationFormats
|
Адреса для поиска представлений, частичных представлений и макетов | ~/Views/{1}/{0}.cshtml ,
~/Views/{1}/{0}.vbhtml ,
~/Views/Shared/{0}.cshtml ,
~/Views/Shared/{0}.vbhtml
|
AreaViewLocationFormats
AreaMasterLocationFormats
AreaPartialViewLocationFormats
|
Адреса для поиска представлений, частичных представлений и макетов для областей | ~/Areas/{2}/Views/{1}/{0}.cshtml ,
~/Areas/{2}/Views/{1}/{0}.vbhtml ,
~/Areas/{2}/Views/Shared/{0}.cshtml ,
~/Areas/{2}/Views/Shared/{0}.vbhtml |
Эти свойства предшествовали появлению Razor, поэтому каждой группе из трех свойств соответствует один набор значений. Каждый набор значений представляет собой массив строк. Ниже приведены значения параметров, которые соответствуют заполнителям:
{0}
- имя представления.{1}
- имя контроллера.{2}
- имя области.
Для изменения адресов поиска создайте новый класс, наследующий от RazorViewEngine
, и измените значения для одного или нескольких свойств, описанных в таблице 18-1.
Чтобы продемонстрировать, как изменять адреса поиска, мы добавили в приложение проект Infrastructure
и создали движок представлений под названием CustomLocationViewEngine
, который показан в листинге 18-12.
Листинг 18-12: Изменяем адреса поиска в Razor
using System.Web.Mvc;
namespace WorkingWithRazor.Infrastructure
{
public class CustomLocationViewEngine : RazorViewEngine
{
public CustomLocationViewEngine()
{
ViewLocationFormats = new string[] {"~/Views/{1}/{0}.cshtml", "~/Views/Common/{0}.cshtml"};
}
}
}
Мы установили новое значение для ViewLocationFormats
. Наш новый массив содержит записи только для файлов .cshtml
. Кроме того, мы изменили каталог для общих представлений на Views/Common
вместо Views/Shared
. Далее мы зарегистрировали наш пользовательский движок, используя коллекцию ViewEngines.Engines
в методе Application_Start
файла Global.asax
, как показано в листинге 18-13.
Листинг 18-13: Регистрируем пользовательский движок представлений
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using WorkingWithRazor.Infrastructure;
namespace WorkingWithRazor
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomLocationViewEngine());
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
Вы должны помнить, что механизм вызова действий обращается к каждому движку по очереди, проверяя, какой из них может найти представление. Когда нам нужно было добавить в коллекцию пользовательский движок, она уже содержала стандартный движок Razor. Чтобы избежать конкуренции с этой реализацией, мы вызвали метод Clear
и удалили все зарегистрированные движки, а затем зарегистрировали наш пользовательский движок с помощью метода Add
.
Чтобы продемонстрировать изменение адресов поиска, мы создали папку /Views/Common
и добавили в нее файл представления под названием List.cshtml
. Содержимое этого файла показано в листинге 18-14.
Листинг 18-14: Содержимое файла /Views/Common/List.cshtml
@{
ViewBag.Title = "List";
}
<h3>This is the /Views/Common/List.cshtml View</h3>
Для отображения этого представления мы добавили в контроллер Home
метод действия, показанный в листинге 18-15.
Листинг 18-15: Добавляем метод действия в контроллер Home
using System.Web.Mvc;
namespace WorkingWithRazor.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
string[] names = {"Apple", "Orange", "Pear"};
return View(names);
}
public ActionResult List()
{
return View();
}
}
}
Если мы запустим приложение и перейдем по ссылке /Home/List
, поиск файла представления List.cshtml
будет осуществляться в добавленной нами папке Views/Common
, как показано на рисунке 18-5.
Рисунок 18-5: Результат добавления новых адресов поиска в движок представления