Создание движка представлений

Последнее обновление: 24.03.2022

Движки представлений - это классы, которые реализуют интерфейс IViewEngine, который определен в пространстве имен Microsoft.AspNetCore.Mvc.ViewEngines:

public interface IViewEngine
{
    ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage);
    ViewEngineResult GetView(string executingFilePath, string viewPath, bool isMainPage);
}

Когда для обработки ответа необходимо представление, то MVC вызывает у движка представления метод GetView(), который предоставляет представление. Если же метод GetView() не может предоставить представление, тогда вызывается метод FindView(), который выполняет поиск представления.

Оба метода возвращают не просто представление, а объект ViewEngineResult, для создания которого мы можем выполнить один из статических методов, определенных в этом же классе ViewEngineResult:

  • Found(name, view): вызов этого метода устанавливает представление через свойство View объекта ViewEngineResult, которое потом используется для обработки запроса

  • NotFound(name, locations): вызов этого метода говорит MVC, что представление не найдено. В качестве параметра передается значение locations, которое представляет перечисление путей, по которым движок искал представление.

Само же представление представляет объект интерфейса IView:

public interface IView 
{
	string Path { get;}
	Task RenderAsync(ViewContext context);
}

Свойство Path возвращает путь к файлу представления. А метод RenderAsync() вызывается для генерации ответа клиенту. В качестве параметра в метод передается объект ViewContext, функционал которого мы можем использовать для рендеринга представлений. В частности, ViewContext определяет следующие свойства:

  • ExecutingFilePath: путь к обрабатываемому файлу представления

  • TempData: возвращает объект TempData

  • ViewBag: возвращает динамический объект - все то, что передается в контроллере через ViewBag

  • ViewData: возвращает словарь ViewDataDictionary, который содержит переданные из контроллера значения, а также модель представления и ее состояние

  • Writer: возвращает объект TextWriter, который применяется для генерации содержимого из представления

Чтобы понять, как складывается взаимодействие всех этих классов, создадим небольшой пример. Создадим новый проект ASP.NET Core по типу ASP.NET Core Empty.

Подключение MVC в пустой проект ASP.NET Core

Далее создадим в проекте новую папку, которую назовем Controllers и которая будет предназначена для контроллеров. Затем добавим в нее простенький контроллер HomeController:

using Microsoft.AspNetCore.Mvc;

namespace MvcApp.Controllers
{
    public class HomeController : Controller
    {
        public ViewResult Index()
        {
            return View();
        }
        public ViewResult About()
        {
            return View("About");
        }
        public ViewResult Contact()
        {
            return View();
        }
    }
}

Все три метода контроллеров используют метод View для использования представлений для генерации ответа. Однако на данный момент у нас нет никаких представлений. Поэтому добавим в проект папку Views.

Затем в эту папку добавим два новых html-файла: index.html и about.html. Определим в этих файлах какое-нибудь содержимое. Например, в моем случае содержимое файла index.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM - Index</title>
</head>
<body>
    <h2>Index Page</h2>
</body>
</html>

Итак, наши представления будут представлять обычные html-файлы.

Далее добавим в проект папку Util для вспомогательных классов.

Создание класса представления

Далее добавим в папку Util новый класс, который назовем CustomView:

using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;

namespace MvcApp.Util
{
    public class CustomView : IView
    {
        public CustomView(string viewPath)
        {
            Path = viewPath;
        }
        public string Path { get; set; }
        public async Task RenderAsync(ViewContext context)
        {
            string content = "";
            using (StreamReader viewReader = new StreamReader(Path))
            {
                content = await viewReader.ReadToEndAsync();
            }
            await context.Writer.WriteAsync(content);
        }
    }
}

Класс представления должен реализовать интерфейс IView. Через конструктор он будет получать путь к файлу представления, а в методе RenderAsync() будет происходить считывание файла. Для создания результата используется параметр ViewContext, который с помощью свойства Writer пишет в выходной поток считанное из файла содержимое.

При необходимости можно производить дополнительную обработку файла, например, вводить в html-файлы выражения на C# подобно тому, как это делается в Razor, только в этом случае также придется писать более сложную логику обработки с соответствующим парсингом. В данном же случае ограничимся более простым примером.

Само представление CustomView пока никак не связано с контроллером и классом ViewResult, и чтобы их связать добавим в папку Util еще один класс CustomViewEngine:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewEngines;

namespace MvcApp.Util
{
    public class CustomViewEngine : IViewEngine
    {
        public ViewEngineResult GetView(string? executingFilePath, string viewPath, bool isMainPage)
        {
            return ViewEngineResult.NotFound(viewPath, new string[] { });
        }
        public ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage)
        {
            string viewPath = $"Views/{viewName}.html"; ;
            if (string.IsNullOrEmpty(viewName))
            {
                viewPath = $"Views/{context.RouteData.Values["action"]}.html";
            }

            if (File.Exists(viewPath))
            {
                return ViewEngineResult.Found(viewPath, new CustomView(viewPath));
            }
            else
            {
                return ViewEngineResult.NotFound(viewName, new string[] { viewPath });
            }
        }
    }
}

Движок представлений реализует интерфейс IViewEngine. В методе GetView() просто возвращаем ViewEngineResult, который указывает, что представление не найдено. В метод ViewEngineResult.NotFound в качестве второго параметра нам надо передать путь, по которому осуществлялся поиск. В данном случае поиск нигде не выполнялся, поэтому массив пуст.

По факту метод GetView() во многом аналогичен методу FindView за тем исключением, что в GetView передается в качестве параметра executingFilePath путь к представлению. А в методе FindView вместо него для поиска представления передается контекст ActionResult. Если же логика поиска и создания представления не зависит от этих параметров, то мы можем использовать один и тот же код в обоих методах.

В методе FindView() формируем путь к представлению, которое у нас находится в папке Views и имеет в качестве расширения файла "html". Параметр "viewName", который применяется для формирования пути, - это название представления, которое передается в контроллере в вызове return View("название представления"). Но мы можем в контроллере и не указывать имя представления. И в этом случае параметр "viewName" будет пустым. Поэтому дополнительно получаем из контекста запроса название действия из параметров маршрута и используем его для поиска представления. Например, если мы обратились к методу Index, то здесь мы получим из параметров маршрута Index.

С учетом всех файлов получится следующий проект:

View Engine in ASP.NET Core MVC и C#

Теперь изменим класс Program.cs:

using Microsoft.AspNetCore.Mvc;
using MvcApp.Util;

var builder = WebApplication.CreateBuilder(args);

// добавляем поддержку контроллеров с представлениями
builder.Services.AddControllersWithViews();

// устанавливаем движок представлений
builder.Services.Configure<MvcViewOptions>(options => {
    options.ViewEngines.Clear();
    options.ViewEngines.Insert(0, new CustomViewEngine());
});

var app = builder.Build();

// устанавливаем сопоставление маршрутов с контроллерами
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Для установки движка представлений применяется объект MvcViewOptions. В MVC может использоваться одновременно несколько движков представлений, чтобы использовать только один, сначала очищаем всю коллекцию движков:

options.ViewEngines.Clear();

Затем добавляем в начало коллекции наш движок:

options.ViewEngines.Insert(0, new CustomViewEngine());

И после запуска движок представлений будет предоставлять нужные html-файлы из папки views:

Движок представлений в ASP.NET Core MVC и C#

Если же нужный html-файл не будет найден, то мы увидим ошибку с перечислением путей, по которым велся поиск файла:

Реализация движка представлений в ASP.NET Core MVC и C#
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850