Это меня заинтересовало, и у меня наконец-то появилась возможность изучить его. Другие люди, по-видимому, не поняли, что это проблема с поиском представления , а не проблема с самой маршрутизацией - и это, вероятно, потому, что заголовок вашего вопроса указывает, что он касается маршрутизации.
В любом случае, поскольку это проблема, связанная с представлением, единственный способ получить то, что вы хотите, - это переопределить механизм представления по умолчанию . Обычно, когда вы делаете это, просто переключите свой движок просмотра (например, на Spark, NHaml и т. Д.). В этом случае, это не логика показов создания нам нужно переопределить, но FindPartialView
и FindView
методы в VirtualPathProviderViewEngine
классе.
Вы можете поблагодарить своих счастливчиков за то, что эти методы на самом деле виртуальные, потому что все остальное в VirtualPathProviderViewEngine
них даже недоступно - оно частное, и поэтому очень раздражает переопределение логики поиска, потому что вам нужно в основном переписать половину кода, который уже был написан, если вы хотите, чтобы он хорошо работал с кешем местоположения и форматами местоположения. Покопавшись в Reflector, я наконец нашел рабочее решение.
То , что я сделал здесь , чтобы сначала создать аннотацию , AreaAwareViewEngine
которая непосредственно вытекает из VirtualPathProviderViewEngine
вместо WebFormViewEngine
. Я сделал это для того, чтобы, если вы хотите вместо этого создавать представления Spark (или что-то еще), вы все равно можете использовать этот класс в качестве базового типа.
Приведенный ниже код довольно многословен, поэтому, чтобы дать вам краткое описание того, что он на самом деле делает: он позволяет вам поместить {2}
в формат местоположения, который соответствует имени области, точно так же, как {1}
имя контроллера. Это оно! Вот для чего нам пришлось написать весь этот код:
BaseAreaAwareViewEngine.cs
public abstract class BaseAreaAwareViewEngine : VirtualPathProviderViewEngine
{
private static readonly string[] EmptyLocations = { };
public override ViewEngineResult FindView(
ControllerContext controllerContext, string viewName,
string masterName, bool useCache)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(viewName))
{
throw new ArgumentNullException(viewName,
"Value cannot be null or empty.");
}
string area = getArea(controllerContext);
return FindAreaView(controllerContext, area, viewName,
masterName, useCache);
}
public override ViewEngineResult FindPartialView(
ControllerContext controllerContext, string partialViewName,
bool useCache)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(partialViewName))
{
throw new ArgumentNullException(partialViewName,
"Value cannot be null or empty.");
}
string area = getArea(controllerContext);
return FindAreaPartialView(controllerContext, area,
partialViewName, useCache);
}
protected virtual ViewEngineResult FindAreaView(
ControllerContext controllerContext, string areaName, string viewName,
string masterName, bool useCache)
{
string controllerName =
controllerContext.RouteData.GetRequiredString("controller");
string[] searchedViewPaths;
string viewPath = GetPath(controllerContext, ViewLocationFormats,
"ViewLocationFormats", viewName, controllerName, areaName, "View",
useCache, out searchedViewPaths);
string[] searchedMasterPaths;
string masterPath = GetPath(controllerContext, MasterLocationFormats,
"MasterLocationFormats", masterName, controllerName, areaName,
"Master", useCache, out searchedMasterPaths);
if (!string.IsNullOrEmpty(viewPath) &&
(!string.IsNullOrEmpty(masterPath) ||
string.IsNullOrEmpty(masterName)))
{
return new ViewEngineResult(CreateView(controllerContext, viewPath,
masterPath), this);
}
return new ViewEngineResult(
searchedViewPaths.Union<string>(searchedMasterPaths));
}
protected virtual ViewEngineResult FindAreaPartialView(
ControllerContext controllerContext, string areaName,
string viewName, bool useCache)
{
string controllerName =
controllerContext.RouteData.GetRequiredString("controller");
string[] searchedViewPaths;
string partialViewPath = GetPath(controllerContext,
ViewLocationFormats, "PartialViewLocationFormats", viewName,
controllerName, areaName, "Partial", useCache,
out searchedViewPaths);
if (!string.IsNullOrEmpty(partialViewPath))
{
return new ViewEngineResult(CreatePartialView(controllerContext,
partialViewPath), this);
}
return new ViewEngineResult(searchedViewPaths);
}
protected string CreateCacheKey(string prefix, string name,
string controller, string area)
{
return string.Format(CultureInfo.InvariantCulture,
":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:",
base.GetType().AssemblyQualifiedName,
prefix, name, controller, area);
}
protected string GetPath(ControllerContext controllerContext,
string[] locations, string locationsPropertyName, string name,
string controllerName, string areaName, string cacheKeyPrefix,
bool useCache, out string[] searchedLocations)
{
searchedLocations = EmptyLocations;
if (string.IsNullOrEmpty(name))
{
return string.Empty;
}
if ((locations == null) || (locations.Length == 0))
{
throw new InvalidOperationException(string.Format("The property " +
"'{0}' cannot be null or empty.", locationsPropertyName));
}
bool isSpecificPath = IsSpecificPath(name);
string key = CreateCacheKey(cacheKeyPrefix, name,
isSpecificPath ? string.Empty : controllerName,
isSpecificPath ? string.Empty : areaName);
if (useCache)
{
string viewLocation = ViewLocationCache.GetViewLocation(
controllerContext.HttpContext, key);
if (viewLocation != null)
{
return viewLocation;
}
}
if (!isSpecificPath)
{
return GetPathFromGeneralName(controllerContext, locations, name,
controllerName, areaName, key, ref searchedLocations);
}
return GetPathFromSpecificName(controllerContext, name, key,
ref searchedLocations);
}
protected string GetPathFromGeneralName(ControllerContext controllerContext,
string[] locations, string name, string controllerName,
string areaName, string cacheKey, ref string[] searchedLocations)
{
string virtualPath = string.Empty;
searchedLocations = new string[locations.Length];
for (int i = 0; i < locations.Length; i++)
{
if (string.IsNullOrEmpty(areaName) && locations[i].Contains("{2}"))
{
continue;
}
string testPath = string.Format(CultureInfo.InvariantCulture,
locations[i], name, controllerName, areaName);
if (FileExists(controllerContext, testPath))
{
searchedLocations = EmptyLocations;
virtualPath = testPath;
ViewLocationCache.InsertViewLocation(
controllerContext.HttpContext, cacheKey, virtualPath);
return virtualPath;
}
searchedLocations[i] = testPath;
}
return virtualPath;
}
protected string GetPathFromSpecificName(
ControllerContext controllerContext, string name, string cacheKey,
ref string[] searchedLocations)
{
string virtualPath = name;
if (!FileExists(controllerContext, name))
{
virtualPath = string.Empty;
searchedLocations = new string[] { name };
}
ViewLocationCache.InsertViewLocation(controllerContext.HttpContext,
cacheKey, virtualPath);
return virtualPath;
}
protected string getArea(ControllerContext controllerContext)
{
// First try to get area from a RouteValue override, like one specified in the Defaults arg to a Route.
object areaO;
controllerContext.RouteData.Values.TryGetValue("area", out areaO);
// If not specified, try to get it from the Controller's namespace
if (areaO != null)
return (string)areaO;
string namespa = controllerContext.Controller.GetType().Namespace;
int areaStart = namespa.IndexOf("Areas.");
if (areaStart == -1)
return null;
areaStart += 6;
int areaEnd = namespa.IndexOf('.', areaStart + 1);
string area = namespa.Substring(areaStart, areaEnd - areaStart);
return area;
}
protected static bool IsSpecificPath(string name)
{
char ch = name[0];
if (ch != '~')
{
return (ch == '/');
}
return true;
}
}
Как уже говорилось, это не конкретный движок, поэтому вы должны его создать. Эта часть, к счастью, намного проще, все, что нам нужно сделать, это установить форматы по умолчанию и фактически создать представления:
AreaAwareViewEngine.cs
public class AreaAwareViewEngine : BaseAreaAwareViewEngine
{
public AreaAwareViewEngine()
{
MasterLocationFormats = new string[]
{
"~/Areas/{2}/Views/{1}/{0}.master",
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.master",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
"~/Views/{1}/{0}.master",
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.master"
"~/Views/Shared/{0}.cshtml"
};
ViewLocationFormats = new string[]
{
"~/Areas/{2}/Views/{1}/{0}.aspx",
"~/Areas/{2}/Views/{1}/{0}.ascx",
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.aspx",
"~/Areas/{2}/Views/Shared/{0}.ascx",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.aspx"
"~/Views/Shared/{0}.ascx"
"~/Views/Shared/{0}.cshtml"
};
PartialViewLocationFormats = ViewLocationFormats;
}
protected override IView CreatePartialView(
ControllerContext controllerContext, string partialPath)
{
if (partialPath.EndsWith(".cshtml"))
return new System.Web.Mvc.RazorView(controllerContext, partialPath, null, false, null);
else
return new WebFormView(controllerContext, partialPath);
}
protected override IView CreateView(ControllerContext controllerContext,
string viewPath, string masterPath)
{
if (viewPath.EndsWith(".cshtml"))
return new RazorView(controllerContext, viewPath, masterPath, false, null);
else
return new WebFormView(controllerContext, viewPath, masterPath);
}
}
Обратите внимание, что мы добавили несколько записей в стандарт ViewLocationFormats
. Это новые {2}
записи, где {2}
они будут сопоставлены с тем, что area
мы добавили в RouteData
. Я оставил в MasterLocationFormats
покое, но, очевидно, вы можете изменить это, если хотите.
Теперь измените ваш, global.asax
чтобы зарегистрировать этот механизм просмотра:
Global.asax.cs
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new AreaAwareViewEngine());
}
... и зарегистрируйте маршрут по умолчанию:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Area",
"",
new { area = "AreaZ", controller = "Default", action = "ActionY" }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
}
Теперь создайте AreaController
только что упомянутый:
DefaultController.cs (в ~ / Controllers /)
public class DefaultController : Controller
{
public ActionResult ActionY()
{
return View("TestView");
}
}
Очевидно, нам нужна структура каталогов и представление, чтобы работать с ними - мы будем делать это очень просто:
TestView.aspx (в ~ / Areas / AreaZ / Views / Default / или ~ / Areas / AreaZ / Views / Shared /)
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<h2>TestView</h2>
This is a test view in AreaZ.
И это все. Наконец, мы закончили .
По большей части вы должны иметь возможность просто взять BaseAreaAwareViewEngine
и AreaAwareViewEngine
и поместить его в любой проект MVC, поэтому, хотя для этого потребовалось много кода, вам нужно написать его только один раз. После этого остается лишь отредактировать несколько строк global.asax.cs
и создать структуру вашего сайта.
ActionLink
проблему, добавив то же самоеarea = "AreaZ"
в сопоставление маршрутов "Default" вglobal.asax.cs
. Хотя я не уверен; попробуйте и посмотрите.Вот как я это сделал. Я не знаю, почему MapRoute () не позволяет вам установить область, но он возвращает объект маршрута, поэтому вы можете продолжать вносить любые дополнительные изменения, которые хотите. Я использую это, потому что у меня есть модульный сайт MVC, который продается корпоративным клиентам, и они должны иметь возможность помещать библиотеки DLL в папку bin для добавления новых модулей. Я разрешаю им изменить "HomeArea" в конфигурации AppSettings.
Изменить: вы также можете попробовать это в своей AreaRegistration.RegisterArea для области, в которую пользователь по умолчанию переходит. Я не тестировал его, но AreaRegistrationContext.MapRoute делает
route.DataTokens["area"] = this.AreaName;
для вас наборы .источник
даже на него уже ответили - это короткий синтаксис (ASP.net 3, 4, 5):
источник
Спасибо Аарону за то, что он указал, что речь идет о поиске просмотров, я неправильно это понял.
[ОБНОВЛЕНИЕ] Я только что создал проект, который по умолчанию отправляет пользователя в область, не вмешиваясь ни в какой код или пути поиска:
В global.asax зарегистрируйтесь как обычно:
в
Application_Start()
убедитесь, что используется следующий порядок;в вашей области регистрации, используйте
Пример можно найти на http://www.emphess.net/2010/01/31/areas-routes-and-defaults-in-mvc-2-rc/
Я очень надеюсь, что это то, о чем вы просили ...
////
Не думаю, что написание псевдонима
ViewEngine
- лучшее решение в данном случае. (Отсутствие репутации, не могу комментировать).WebFormsViewEngine
Является Область известно и содержитAreaViewLocationFormats
, определенный по умолчанию , так какЯ считаю, что вы не придерживаетесь этой конвенции. Вы опубликовали
как рабочий хак, но это должно быть
ЕСЛИ вы не хотите следовать соглашению, однако, вы можете выбрать короткий путь, производный от
WebFormViewEngine
(например, в MvcContrib), где вы можете установить пути поиска в конструкторе, или -a маленький хакер - указав свое соглашение следующим образомApplication_Start
:Конечно, это следует делать немного осторожнее, но я думаю, что это показывает идею. Эти поля
public
вVirtualPathProviderViewEngine
в MVC 2 RC.источник
VirtualPathProviderViewEngine
не имеет этого свойства и не зависит от области. И хотя этот вопрос действительно был заявлен о MVC 2, многие люди до сих пор его не используют (и не будут использовать в течение некоторого времени). Итак, ваш ответ на конкретный вопрос проще, но мой - единственный, который подойдет пользователям MVC1, которые наткнулись на этот вопрос. Мне нравится давать ответы, не зависящие от предварительных версий функций, которые могут быть изменены.RegisterAreas
пойти раньшеRegisterRoutes
. Интересно, почему мой код внезапно перестал работать, и заметил этот рефакторинг;)Я предполагаю, что вы хотите, чтобы пользователь был перенаправлен на
~/AreaZ
URL-адрес после того, как он посетил~/
URL-адрес. Я бы добился этого с помощью следующего кода в вашем корнеHomeController
.И следующий маршрут в
Global.asax
.источник
Во-первых, какую версию MVC2 вы используете? От preview2 до RC произошли значительные изменения.
Предполагая, что вы используете RC, я думаю, что отображение маршрутов должно выглядеть иначе. В
AreaRegistration.cs
вашем районе вы можете зарегистрировать какой-нибудь маршрут по умолчанию, напримерПриведенный выше код отправит пользователя
MyRouteController
в нашShopArea
по умолчанию.Использование пустой строки в качестве второго параметра должно вызывать исключение, поскольку необходимо указать контроллер.
Конечно, вам придется изменить маршрут по умолчанию,
Global.asax
чтобы он не мешал этому маршруту по умолчанию, например, используя префикс для основного сайта.Также см. Эту ветку и ответ Хаака: MVC 2 AreaRegistration Routes Order
Надеюсь это поможет.
источник
Добавление следующего в мой Application_Start работает для меня, хотя я не уверен, есть ли у вас этот параметр в RC:
источник
Чтобы заставить это работать, я сделал следующее:
В контроллер я добавил следующий код:
В моем RouterConfig.cs я добавил следующее:
Уловка всего этого заключается в том, что я создал конструктор по умолчанию, который всегда будет контроллером запуска при каждом запуске моего приложения. Когда он попадает в этот контроллер по умолчанию, он перенаправляется на любой контроллер, который я укажу в действии индекса по умолчанию. Что в моем случае
,
источник
Вы пробовали это?
источник
Поиск различных строительных блоков выполняется в жизненном цикле запроса. Один из первых шагов в жизненном цикле запроса ASP.NET MVC - сопоставление запрошенного URL-адреса с правильным методом действия контроллера. Этот процесс называется маршрутизацией. Маршрут по умолчанию инициализируется в файле Global.asax и описывает для платформы ASP.NET MVC, как обрабатывать запрос. Двойной щелчок по файлу Global.asax в проекте MvcApplication1 отобразит следующий код:
В обработчике событий Application_Start (), который запускается при компиляции приложения или перезапуске веб-сервера, регистрируется таблица маршрутов. Маршрут по умолчанию называется Default и отвечает на URL-адрес в форме http://www.example.com/ {controller} / {action} / {id}. Переменные между {и} заполняются фактическими значениями из URL-адреса запроса или значениями по умолчанию, если в URL-адресе нет переопределения. Этот маршрут по умолчанию будет отображаться на контроллер Home и метод действия Index в соответствии с параметрами маршрутизации по умолчанию. У нас не будет никаких других действий с этой картой маршрутизации.
По умолчанию все возможные URL-адреса могут быть сопоставлены через этот маршрут по умолчанию. Также возможно создание собственных маршрутов. Например, сопоставим URL http://www.example.com/Employee/Maarten с контроллером Employee, действием Show и параметром firstname. Следующий фрагмент кода можно вставить в только что открытый файл Global.asax. Поскольку платформа ASP.NET MVC использует первый совпадающий маршрут, этот фрагмент кода следует вставить над маршрутом по умолчанию; в противном случае маршрут никогда не будет использоваться.
Теперь добавим необходимые компоненты для этого маршрута. Прежде всего, создайте класс с именем EmployeeController в папке Controllers. Вы можете сделать это, добавив новый элемент в проект и выбрав шаблон класса контроллера MVC, расположенный в Web | Категория MVC. Удалите метод действия Index и замените его методом или действием с именем Show. Этот метод принимает параметр firstname и передает данные в словарь ViewData. Этот словарь будет использоваться представлением для отображения данных.
Класс EmployeeController передаст в представление объект Employee. Этот класс Employee должен быть добавлен в папку Models (щелкните эту папку правой кнопкой мыши и выберите в контекстном меню Добавить | Class). Вот код для класса Employee:
источник
Что ж, хотя создание пользовательского механизма просмотра может работать для этого, у вас все же может быть альтернатива:
Ура!
источник
Принятое решение этого вопроса, хотя и верно в изложении того, как создать настраиваемый механизм представления, не дает правильного ответа на этот вопрос. Проблема здесь в том, что Пино неправильно указывает свой маршрут по умолчанию . В частности, его определение «площади» неверно. "Area" проверяется с помощью коллекции DataTokens и должна быть добавлена как таковая:
Указанная «область» в объекте по умолчанию будет проигнорирована . Приведенный выше код создает маршрут по умолчанию, который перехватывает запросы к корню вашего сайта, а затем вызывает контроллер по умолчанию, действие индекса в области администратора. Также обратите внимание, что к DataTokens добавляется ключ «Namespaces», это требуется только в том случае, если у вас несколько контроллеров с одинаковым именем. Это решение проверено с Mvc2 и Mvc3 .NET 3.5 / 4.0
источник
ммм, я не знаю, зачем все это программирование, я думаю, что исходная проблема легко решается указанием этого маршрута по умолчанию ...
источник