Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ASP_NET_MVC_4_Framework_s_primerami_na_C_dlya_p

.pdf
Скачиваний:
25
Добавлен:
19.03.2016
Размер:
17.66 Mб
Скачать

параметры, в которых указывается запрос и обработавший его контроллер (объект ControllerContext), имя представления и его макет, а также сообщение о том, может ли движок представлений использовать предыдущий результат из кэша. Эти методы вызываются во время обработки ViewResult. Последний метод, ReleaseView, вызывается, когда представление больше не требуется.

Примечание

Поддержка движков представлений в MVC Framework реализуется классом ControllerActionInvoker, который является встроенной реализацией интерфейса IActionInvoker, описанного в главе 15. Вы не сможете автоматически

использовать движки представлений, если вы реализовали пользовательский механизм вызова действий или фабрику контроллеров непосредственно из интерфейсов IActionInvoker или IControllerFactory.

Класс ViewEngineResult позволяет движку представлений отвечать на запрос представления. Он показан в листинге 18-2.

Листинг 18-2: Класс ViewEngineResult

using System.Collections.Generic;

namespace System.Web.Mvc

{

public class ViewEngineResult

{

public ViewEngineResult(IEnumerable<string> searchedLocations)

{

if (searchedLocations == null)

{

throw new ArgumentNullException("searchedLocations");

}

SearchedLocations = searchedLocations;

}

public ViewEngineResult(IView view, IViewEngine viewEngine)

{

if (view == null)

{

throw new ArgumentNullException("view");

}

if (viewEngine == null)

{

throw new ArgumentNullException("viewEngine");

}

View = view;

ViewEngine = viewEngine;

}

public IEnumerable<string> SearchedLocations { get; private set; } public IView View { get; private set; }

public IViewEngine ViewEngine { get; private set; }

}

}

Для создания результата необходимо выбрать один из двух конструкторов. Если ваш движок возвращает представления в ответ на запросы, то ViewEngineResult создается помощью следующего конструктора:

451

public ViewEngineResult(IView view, IViewEngine viewEngine)

Его параметрами являются реализация интерфейса IView и движок представления (так что позже будет вызван метод ReleaseView). Если ваш движок не возвращает представления на запросы, то используйте следующий конструктор:

public ViewEngineResult(IEnumerable<string> searchedLocations)

Здесь параметром является перечисление мест для поиска представления. Если представление не может быть найдено, пользователю будет показано соответствующее сообщение, которое мы рассмотрим позже.

Примечание

Вам не кажется, что класс ViewEngineResult какой-то громоздкий? Выводить

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

Последним структурным элементом движка представления является интерфейс IView, который показан в листинге 18-3.

Листинг 18-3: Интерфейс IView

using System.IO;

namespace System.Web.Mvc

{

public interface IView

{

void Render(ViewContext viewContext, TextWriter writer);

}

}

Мы передаем реализацию IView в конструктор объекта ViewEngineResult, который будет позже возвращен из методов движка представления. MVC Framework вызывает метод Render. Параметр ViewContext предоставляет информацию о запросе клиента и выводе метода действия. Параметр TextWriter записывает ответ клиенту.

Как мы уже говорили, самый простой способ изучить работу всех этих элементов - IViewEngine, IView и ViewEngineResult - это создать пользовательский движок представлений. Мы напишем простой движок, который возвращает только один вид представления. Оно будет визуализировать результат, который содержит информацию о запросе и данные представления, сгенерированные методом действия. Таким образом мы сможем продемонстрировать, как работают движки представлений, не погружаясь в анализ шаблонов представлений.

Создаем пример проекта

В качестве примера для этой главы мы создали проект под названием Views, используя шаблон Empty. В нем был создан контроллер Home, который вы можете увидеть в листинге 18-4.

452

Листинг 18-4: Контроллер Home в проекте Views

using System;

using System.Web.Mvc;

namespace Views.Controllers

{

public class HomeController : Controller

{

public ActionResult Index()

{

ViewData["Message"] = "Hello, World"; ViewData["Time"] = DateTime.Now.ToShortTimeString(); return View("DebugData");

}

public ActionResult List()

{

return View();

}

}

}

Для этого проекта мы не создали ни одного представления, потому что собираемся реализовывать пользовательский движок, а не использовать Razor.

Создаем пользовательский IView

Начнем с создания пользовательской реализации IView. Мы добавили в пример проекта папку Infrastructure и создали в ней класс под названием DebugDataView, который показан в листинге

18-5.

Листинг 18-5: Пользовательская реализация IView

using System.IO; using System.Web.Mvc;

namespace Views.Infrastructure

{

public class DebugDataView : IView

{

public void Render(ViewContext viewContext, TextWriter writer)

{

Write(writer, "---Routing Data---");

foreach (string key in viewContext.RouteData.Values.Keys)

{

Write(writer, "Key: {0}, Value: {1}", key, viewContext.RouteData.Values[key]);

}

Write(writer, "---View Data---");

foreach (string key in viewContext.ViewData.Keys)

{

Write(writer, "Key: {0}, Value: {1}", key, viewContext.ViewData[key]);

}

}

private void Write(TextWriter writer, string template, params object[] values)

{

writer.Write(string.Format(template, values) + "<p/>");

}

}

}

453

Как видите, в методе Render этого представления используются два параметра: мы берем значения из ViewContext и записываем ответ клиенту с помощью TextWriter. Немного позже вы увидите, как функционирует этот класс.

Создаем реализацию IViewEngine

Следует помнить, что главная задача движка представления - создавать объект ViewEngineResult, который содержит либо IView, либо список мест для поиска подходящего представления. Теперь, когда у нас есть реализация IView, мы можем создать движок представления. В папке Infrastructure мы создали новый класс под названием DebugDataViewEngine.cs, содержание которого приведено в листинге 18-6.

Листинг 18-6: Пользовательская реализация IViewEngine

using System.Web.Mvc;

namespace Views.Infrastructure

{

public class DebugDataViewEngine : IViewEngine

{

public ViewEngineResult FindView(ControllerContext controllerContext, string viewName,

string masterName, bool useCache)

{

if (viewName == "DebugData")

{

return new ViewEngineResult(new DebugDataView(), this);

}

else

{

return new ViewEngineResult(new string[] {"No view (Debug Data View Engine)"});

}

}

public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName,

bool useCache)

{

return new ViewEngineResult(new string[] {"No view (Debug Data View Engine)"});

}

public void ReleaseView(ControllerContext controllerContext, IView view)

{

// do nothing

}

}

}

Мы реализуем поддержку только одного представления, которое называется DebugData. При получении запроса к нему мы будем возвращать экземпляр пользовательской реализации IView, как показано далее:

return new ViewEngineResult(new DebugDataView(), this);

Если бы мы реализовывали более серьезный движок, то использовали бы возможность поиска шаблонов, учитывали макет и предоставляли параметры кэширования. Но в нашем простом примере требуется только создавать новый экземпляр класса DebugDataView. Если мы получим запрос к другому представлению (не DebugData), то вернем ViewEngineResult, как показано далее:

454

return new ViewEngineResult(new string[] { "No view (Debug Data View Engine)" });

Интерфейс IViewEngine предполагает, что движок знает, где искать представления. Это разумное предположение, так как представления обычно представляют собой шаблоны, которые хранятся в проекте как отдельные файлы. В данном случае нам не нужно ничего искать, и мы просто возвращаем фиктивное месторасположение, указывающее на то, что мы не можем найти запрошенное представление.

Наш пользовательский движок не поддерживает частичные представления, поэтому метод FindPartialView тоже будет возвращать результат, указывающий, что мы не можем найти нужное представление. Далее в этой главе мы вернемся к частичным представлениям и рассмотрим, как они обрабатываются в движке Razor. Метод ReleaseView пока еще ничего не делает, потому что в нашей реализации IView еще нет ресурсов, с которыми он мог бы работать.

Регистрируем пользовательский движок представлений

Движок представлений регистрируется в методе Application_Start файла Global.asax, как показано в листинге 18-7.

Листинг 18-7: Регистрируем пользовательский движок представлений в Global.asax

using System.Web.Http; using System.Web.Mvc; using System.Web.Routing;

using Views.Infrastructure;

namespace Views

{

public class MvcApplication : System.Web.HttpApplication

{

protected void Application_Start()

{

AreaRegistration.RegisterAllAreas();

ViewEngines.Engines.Add(new DebugDataViewEngine());

WebApiConfig.Register(GlobalConfiguration.Configuration);

FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

RouteConfig.RegisterRoutes(RouteTable.Routes);

}

}

}

MVC Framework поддерживает возможность установки нескольких движков в одном приложении – их перечень содержится в статической коллекции ViewEngine.Engines. При обработке ViewResult механизм вызова действий получает набор установленных движков и вызывает их методы FindView по очереди.

Когда механизм вызова действий получает объект ViewEngineResult, содержащий IView, он прекращает вызывать методы FindView. Это значит, что если два и более движка могут обслужить запрос к одному представлению, то решающее значение имеет последовательность, в которой они добавлены в коллекцию ViewEngines.Engines. Если вы хотите назначить вашему движку более высокий приоритет, вставьте его в начале коллекции, как показано здесь:

ViewEngines.Engines.Insert(0, new DebugDataViewEngine());

455

Тестируем движок представлений

Сейчас мы уже в состоянии протестировать наш движок представлений. Если запустить приложение, браузер автоматически перейдет по корневому URL проекта, который будет соотнесен с действием Index контроллера Home. Используя метод View, метод действия возвращает ViewResult, который содержит представление DebugData. Результат показан на рисунке 18-1.

Рисунок 18-1: Использование пользовательского движка представлений

Это результат вызова метода FindView для представления, которое мы в состоянии обработать. Если мы перейдем по ссылке /Home/List, MVC Framework вызовет метод действия List, который вызовет метод View для запроса своего представления по умолчанию, которое мы не поддерживаем.

Результат показан на рисунке 18-2.

Рисунок 18-2: Запрос неподдерживаемого представления

456

Как видите, в нашем сообщении отображается список адресов для поиска представления. Обратите внимание, что представления Razor и ASPX также появляются в списке, так как эти движки попрежнему используются. Если мы хотим использовать только наш пользовательский движок, то перед его регистрацией в файле Global.asax нужно вызвать метод Clear, как показано в листинге

18-8.

Листинг 18-8: Удаляем другие движки представлений

using System.Web.Http; using System.Web.Mvc; using System.Web.Routing;

using Views.Infrastructure;

namespace Views

{

public class MvcApplication : System.Web.HttpApplication

{

protected void Application_Start()

{

AreaRegistration.RegisterAllAreas();

ViewEngines.Engines.Clear();

ViewEngines.Engines.Add(new DebugDataViewEngine());

WebApiConfig.Register(GlobalConfiguration.Configuration);

FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

RouteConfig.RegisterRoutes(RouteTable.Routes);

}

}

}

Если мы перезапустим приложение и перейдем по ссылке /Home/List, будет использоваться только наш пользовательский движок, как показано на рисунке 18-3.

Рисунок 18-3: Использование только пользовательского движка

457

Работа с движком представления 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>

}

458

Основы визуализации представлений в 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[]>

{

459

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.

460

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]