ASP.NET MVC 4

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: Результат добавления новых адресов поиска в движок представления
или RSS канал: Что новенького на smarly.net