У нас есть очень простой проект 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.