Давайте разберемся как работают движки представлений в ASP.NET MVC 3. Лучший для этого способ – написать свою реализацию. Причем эта задача не такая сложная, как может показаться на первый взгляд.
Для начала создадим пустое (Empty) ASP.NET MVC 3 веб-приложение. Не будем останавливаться на этом шаге подробно. Стоит только отметить название проекта – CustomMVCViewEngineDemo.
Кроме того, вполне логично разместить классы создаваемого движка представлений в отдельной сборке. Однако, в целях упрощения, ограничимся отдельной папкой CustomViewEngine.
Структура движка представлений
В ASP.NET MVC 3 можно выделить две составляющие движка представлений:
- IViewEngine – непосредственно движок. В его задачи входит поиск и создание необходимых Представлений. Кроме того, он может обеспечивать, например, их кэширование.
- IView – Преставления, которые должны обеспечивать генерацию HTML кода страницы.
Реализацией этих двух интерфейсов и займемся.
Реализация движка представлений
1. Представления
Разработку начнем с реализации Представления, т.к. именно оно генерирует HTML код. Для этого в папке CustomViewEngine создадим класс SimpleView. Он реализует единственный метод интерфейса IView:
public void Render(ViewContext viewContext, TextWriter writer);
Задача Render() – записать HTML код страницы в writer, используя данные из контекста viewContext.
Определим язык создаваемых Представлений: для любого слова, заключенного в фигурные скобки, будет осуществляться замена на значение из массива ViewData. Например, {Message} – на содержимое ViewData["Message"]. Остальной код файла разметки будет выводится без изменений.
namespace CustomMVCViewEngineDemo.CustomViewEngine
{
using System.IO;
using System.Text.RegularExpressions;
using System.Web.Mvc;
internal class SimpleView : IView
{
private readonly string _viewPath;
public SimpleView(string viewPath)
{
this._viewPath = viewPath;
}
#region IView Members
public void Render(ViewContext viewContext, TextWriter writer)
{
string content = File.ReadAllText(this._viewPath);
string parsedContents = this.Parse(content, viewContext.ViewData);
writer.Write(parsedContents);
}
#endregion
private string Parse(string content, ViewDataDictionary viewDataDictionary)
{
return Regex.Replace(
content,
"\\{(.+)\\}",
m => this.MatchEvaluator(m, viewDataDictionary));
}
private string MatchEvaluator(Match match, ViewDataDictionary viewData)
{
if (!match.Success) {
return string.Empty;
}
string key = match.Result("$1");
if (!viewData.ContainsKey(key)) {
return string.Empty;
}
return viewData[key].ToString();
}
}
}
2. Движок представлений
Теперь остается только реализовать IViewEngine. Здесь можно сильно упростить задачу. Дело в том, что в ASP.NET MVC 3 существует абстрактный класс VirtualPathProviderViewEngine. Он обеспечивает поиск нужных Представлений и их кэширование. Необходимо только переопределить два метода:
- CreatePartialView() – обеспечивает создание частичного Представления;
- CreateView() – создает Представления, в том числе и на основе шаблона разметки (MasterPage).
Кстати, именно VirtualPathProviderViewEngine является базовым классом для реализаций таких движков представлени�� как WebForms и Razor.
Еще один важный момент. В конструкторе SimpleViewEngine необходимо определить пути, по которым будет осуществляться поиск файлов с разметкой (свойства PartialViewLocationFormats и ViewLocationFormats). В данном примере будем использовать стандартные варианты, определив только свое расширение ".sve".
Создадим в папке CustomViewEngine файл, в котором разместим код класса SimpleViewEngine:
namespace CustomMVCViewEngineDemo.CustomViewEngine
{
using System.Web.Mvc;
public class SimpleViewEngine : VirtualPathProviderViewEngine
{
public SimpleViewEngine()
{
this.ViewLocationFormats = new string[] {
"~/Views/{1}/{0}.sve",
"~/Views/Shared/{0}.sve"
};
this.PartialViewLocationFormats = new string[] {
"~/Views/{1}/{0}.sve",
"~/Views/Shared/{0}.sve"
};
}
protected override IView CreatePartialView(
ControllerContext controllerContext, string partialPath)
{
var path = controllerContext.HttpContext.Server.MapPath(partialPath);
return new SimpleView(path);
}
protected override IView CreateView(
ControllerContext controllerContext, string viewPath, string masterPath)
{
var path = controllerContext.HttpContext.Server.MapPath(viewPath);
return new SimpleView(path);
}
}
}
Все готово.
Тестирование
Проверим созданный движок представлений в работе. Для этого необходимо его зарегистрировать в веб-приложении. Сделаем это, добавив строку в метод Application_Start():
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ViewEngines.Engines.Add(new SimpleViewEngine());
}
Теперь создадим Контроллер Home в соответствующей папке. В действии Index определим значение для строки {Message}:
namespace CustomMVCViewEngineDemo.Controllers
{
using System.Web.Mvc;
public class HomeController : Controller
{
// GET: /Home/
public ActionResult Index()
{
this.ViewData["Message"] = "Custom ASP.NET MVC 3 ViewEngine Demo";
return this.View();
}
}
}
Создадим Представление Index.sve в папке Views/Home:
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div>{Message}</div>
</body>
</html>
Запустим веб-приложение и убедимся в его работоспособности. В браузере будет выведена заданная в Контроллере строка.
Разумеется, созданный движок представлений очень простой. В нем нет, например, поддержки MasterPages и у него очень простой язык. Однако, его разработка позволила лучше понять принципы функционирования ASP.NET MVC 3.
Исходный код проекта (C#, Visual Studio 2010):
CustomMVCViewEngineDemo.zip