MVC 5 – Отображение представлений из подпапок

Вопросы и ответы

У нас есть очень простой проект MVC, который включает контроллеры EmployeeController и StudentController. В папке Views находятся соответствующие папки Employee и Student.

В папке представлений Employees находятся следующие файлы:

~/View/Employee/Index.cshtml
~/View/Employee/PageNotFound.cshtml
~/View/Employee/Test.cshtml

В папке представлений Student находятся следующие файлы:

~/View/Student/Index.cshtml
~/View/Student/PageNotFound.cshtml
~/View/Student/Test.cshtml
~/View/Student/About/Index.cshtml

Что касается представлений Student, то контроллера About нет. Причина в том, что существует CMS, которая публикует файлы .cshtml (возможно, в подпапках) как в папке представлений Student, так и в папке Employee на веб-сервере.

Поэтому мне нужно найти способ загружать любое представление в любой подпапке ~Views/Student с помощью StudentController. Я нашел ряд статей, в которых описано, как это можно сделать. Я обнаружил, что представление не должно иметь определенный контроллер, так как нашел следующий код:

        protected override void HandleUnknownAction(string actionName)
    {
        try
        {
            this.View(actionName).ExecuteResult(this.ControllerContext);
        }
        catch (InvalidOperationException )
        {
            Response.Redirect("~/Student/PageNotFound");
        }
    }

Хотя в StudentController нет действия “Test”, я могу отобразить https://localhost:44367/Student/Test через HandleUnknownAction(). Проблемы начинаются, когда пытаюсь отобразить представления в подпапках, например, https://localhost:44367/Student/About/Index.cshtml

Могу ли я указать пользовательское местоположение для “поиска представлений” в ASP.NET MVC? рассматривает эту ситуацию, поэтому я попробовал:

            System.Web.Mvc.RazorViewEngine rve = (RazorViewEngine)ViewEngines.Engines
          .Where(e => e.GetType() == typeof(RazorViewEngine))
          .FirstOrDefault();

        string[] additionalPartialViewLocations = new[] {
              "~/Views/Student/About/{0}.cshtml"
            };

        if (rve != null)
        {
            rve.PartialViewLocationFormats = rve.PartialViewLocationFormats
              .Union(additionalPartialViewLocations)
              .ToArray();

            rve.ViewLocationFormats = rve.ViewLocationFormats
              .Union(additionalPartialViewLocations)
              .ToArray();

        }

Я жестко запрограммировал путь “~/Views/Student/About/{0}.cshtml”, надеясь сказать “Смотрите в этой папке”. Все равно получаю 404 на https://localhost:44367/Student/About/Index.cshtml. Я отладил HandleUnknownAction() и заметил, что параметр {string actionName} равен “About” для этого URL, я полагаю, что ожидал, что он будет “Index”.

Затем я подумал, что могу изменить маршрутизацию и придумал следующее в RouteConfig.cs

            routes.MapRoute(
            name: "Student",
            url: "Student/{*url}",
            defaults: new { controller = "Student", action = "Index" }
        );

Таким образом, любой запрос ниже Student сопоставляется с одним действием Index в StudentController. Это приближает меня к тому, что я ищу, но теперь я ответственен за поиск физических файлов по URL, проверку их существования, перенаправление на PageNotFound.cshtml в противном случае и т.д. Кажется, что это противоречит всей сущности MVC.

Это проблема маршрутизации? Нужно ли мне создать пользовательский RazorViewEngine? Мы ожидаем, что у нас будет много файлов .cshtml, сгруппированных в подпапках под Student и Employee, мне нужно каким-то образом загружать эти представления. Неправильно ожидать что-то вроде ~Views/Student/About/Subfolder1/Subfolder2/Applying.cshtml

Буду признателен за любую помощь по этому поводу!

Ответ

Вам необходимо реализовать механизм рендеринга представлений из подпапок во Views/Student с использованием существующего StudentController. Давайте рассмотрим, как это можно сделать, vonky в особенности учтем использование метода HandleUnknownAction и настройку путей представлений.

1. Настройка маршрутизации

Первым шагом будет настройка маршрутов в RouteConfig.cs. Мы можем создать один маршрут, который будет захватывать все запросы, начинающиеся с /Student/, и перенаправлять их на метод Index. Важно, чтобы при этом мы могли передать дополнительную информацию о запрашиваемом представлении. Вот пример настройки маршрута:

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "Student",
            url: "Student/{*url}",
            defaults: new { controller = "Student", action = "Index" }
        );
    }
}

Здесь мы используем {*url}, который позволяет передавать все оставшиеся части URL.

2. Обработка неизвестных действий

Теперь мы можем использовать метод HandleUnknownAction в StudentController для обработки вызовов, которые не соответствуют существующему действию. Мы можем извлечь части URL и использовать их для поиска представлений в подпапках.

Пример кода:

public class StudentController : Controller
{
    protected override void HandleUnknownAction(string actionName)
    {
        string url = string.Join("/", RouteData.Values["url"].ToString().Split('/').Skip(1));
        string viewName = string.IsNullOrEmpty(url) ? "Index" : url; // если ни один URL не указан, использовать Index

        try
        {
            View(viewName).ExecuteResult(this.ControllerContext);
        }
        catch (InvalidOperationException)
        {
            Response.Redirect("~/Student/PageNotFound");
        }
    }
}

В этом коде мы извлекаем полный запрашиваемый путь из RouteData.Values["url"], разбиваем его на части и собираем их обратно, чтобы определиться с тем, какое представление пытаться отобразить.

3. Настройка RazorViewEngine (при необходимости)

Если у вас много вложенных папок и вам нужно добавить дополнительные строковые массивы представлений, вы можете настроить ViewEngines для включения всех необходимых путей. Например:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RouteConfig.RegisterRoutes(RouteTable.Routes);

    // Добавляем дополнительные пути для представлений
    var rve = (RazorViewEngine)ViewEngines.Engines.First(e => e is RazorViewEngine);
    rve.ViewLocationFormats = rve.ViewLocationFormats.Union(new[] 
    {
        "~/Views/Student/{1}/{0}.cshtml", // Общий путь для подпапок
        "~/Views/Student/About/{0}.cshtml",
        "~/Views/Student/{0}.cshtml"       // Корневая папка
    }).ToArray();
}

Здесь мы расширяем ViewLocationFormats, чтобы включить поиски представлений в любых подпапках Student.

Заключение

Таким образом, с помощью правильной настройки маршрутизации и переопределения метода HandleUnknownAction, вы можете динамически рендерить представления из любых подпапок в папке Views/Student. Если правильные настройки размещения представлений будут поставлены на начальных этапах, вы сможете легко управлять представлениями, автоматически обрабатывая их с помощью MVC.

Оцените материал
Добавить комментарий

Капча загружается...