Обеспечение строчных URL-адреса довольно легко с .NET 4.5, до тех пор, как вы используете URL Helper для создания ваших URL-адресов:
public static void RegisterRoutes(RouteCollection routes)
{
routes.LowercaseUrls = true;
...
}
RouteCollection.LowercaseUrls
Кроме того, он, как правило, рекомендуется использовать более строгие URL-адреса для улучшения поддержки SEO и кеширования.
Я подозреваю, что это не сообщалось до сих пор, потому что большинство людей используют @Html.ActionLink
, @Html.RouteLink
или @Html.Url
помощников для создания своей URL, который всегда делает их тот же случай, так что сравнение всегда будет работать.
Я был бы признателен, если вы сообщите об этом как о новой проблеме @GitHub, поэтому мы можем добавить это в рабочую очередь, так как я согласен, что это ошибка, которую необходимо решить.
Основная проблема имеет два аспекта:
- карта сайта сохраняет URL-адреса в качестве ключа в одном из словарей, который делает случай чувствительный матч.
- Путь к URL-адресу сравнивается (с учетом регистра) перед тем, как сравниваются нечувствительные к регистру маршруты.
Это означает, что если все URL-адреса были сгенерированы в нижнем регистре, проблема исчезнет. Это связано с тем, что MvcSiteMapProvider использует класс MVC UrlHelper для генерации URL-адресов.
Однако еще одно потенциальное исправление будет заключаться в том, чтобы изменить порядок, в котором выполняются сравнения, чтобы сначала сравнивать маршруты. Вы можете выполнить это путем наследования RequestCacheableSiteMap и переопределения FindSiteMapNode().
// Original
protected virtual ISiteMapNode FindSiteMapNode(HttpContextBase httpContext)
{
// Match RawUrl
var node = this.FindSiteMapNodeFromRawUrl(httpContext);
// Try MVC
if (node == null)
{
node = this.FindSiteMapNodeFromMvc(httpContext);
}
// Try ASP.NET Classic (for interop)
if (node == null)
{
node = this.FindSiteMapNodeFromAspNetClassic(httpContext);
}
// Try the path without the querystring
if (node == null)
{
node = this.FindSiteMapNode(httpContext.Request.Path);
}
// Check accessibility
return this.ReturnNodeIfAccessible(node);
}
// Fixed
protected override ISiteMapNode FindSiteMapNode(HttpContextBase httpContext)
{
// Match MVC
var node = this.FindSiteMapNodeFromMvc(httpContext);
// Try RawUrl
if (node == null)
{
node = this.FindSiteMapNodeFromRawUrl(httpContext);
}
// Try ASP.NET Classic (for interop)
if (node == null)
{
node = this.FindSiteMapNodeFromAspNetClassic(httpContext);
}
// Try the path without the querystring
if (node == null)
{
node = this.FindSiteMapNode(httpContext.Request.Path);
}
// Check accessibility
return this.ReturnNodeIfAccessible(node);
}
Вы можете либо наследовать SiteMapFactory или создать свой собственный ISiteMapFactory, который возвращает тип SiteMap и ввести его с внешним DI. Это решение не будет работать, если вы используете внутренний контейнер DI.
using System;
using MvcSiteMapProvider.Builder;
using MvcSiteMapProvider.Web.Mvc;
using MvcSiteMapProvider.Web;
public class MySiteMapFactory
: ISiteMapFactory
{
public MySiteMapFactory(
ISiteMapPluginProviderFactory pluginProviderFactory,
IMvcResolverFactory mvcResolverFactory,
IMvcContextFactory mvcContextFactory,
ISiteMapChildStateFactory siteMapChildStateFactory,
IUrlPath urlPath,
IControllerTypeResolverFactory controllerTypeResolverFactory,
IActionMethodParameterResolverFactory actionMethodParameterResolverFactory
)
{
if (pluginProviderFactory == null)
throw new ArgumentNullException("pluginProviderFactory");
if (mvcResolverFactory == null)
throw new ArgumentNullException("mvcResolverFactory");
if (mvcContextFactory == null)
throw new ArgumentNullException("mvcContextFactory");
if (siteMapChildStateFactory == null)
throw new ArgumentNullException("siteMapChildStateFactory");
if (urlPath == null)
throw new ArgumentNullException("urlPath");
if (controllerTypeResolverFactory == null)
throw new ArgumentNullException("controllerTypeResolverFactory");
if (actionMethodParameterResolverFactory == null)
throw new ArgumentNullException("actionMethodParameterResolverFactory");
this.pluginProviderFactory = pluginProviderFactory;
this.mvcResolverFactory = mvcResolverFactory;
this.mvcContextFactory = mvcContextFactory;
this.siteMapChildStateFactory = siteMapChildStateFactory;
this.urlPath = urlPath;
this.controllerTypeResolverFactory = controllerTypeResolverFactory;
this.actionMethodParameterResolverFactory = actionMethodParameterResolverFactory;
}
protected readonly ISiteMapPluginProviderFactory pluginProviderFactory;
protected readonly IMvcResolverFactory mvcResolverFactory;
protected readonly IMvcContextFactory mvcContextFactory;
protected readonly ISiteMapChildStateFactory siteMapChildStateFactory;
protected readonly IUrlPath urlPath;
protected readonly IControllerTypeResolverFactory controllerTypeResolverFactory;
protected readonly IActionMethodParameterResolverFactory actionMethodParameterResolverFactory;
#region ISiteMapFactory Members
public virtual ISiteMap Create(ISiteMapBuilder siteMapBuilder, ISiteMapSettings siteMapSettings)
{
var routes = mvcContextFactory.GetRoutes();
var requestCache = mvcContextFactory.GetRequestCache();
// IMPORTANT: We need to ensure there is one instance of controllerTypeResolver and
// one instance of ActionMethodParameterResolver per SiteMap instance because each of
// these classes does internal caching.
var controllerTypeResolver = controllerTypeResolverFactory.Create(routes);
var actionMethodParameterResolver = actionMethodParameterResolverFactory.Create();
var mvcResolver = mvcResolverFactory.Create(controllerTypeResolver, actionMethodParameterResolver);
var pluginProvider = pluginProviderFactory.Create(siteMapBuilder, mvcResolver);
return new MySiteMap(
pluginProvider,
mvcContextFactory,
siteMapChildStateFactory,
urlPath,
siteMapSettings,
requestCache);
}
#endregion
}
А затем впрыснуть ваш завод что-то вроде этого в модуле MvcSiteMapProvider DI (пример StructureMap показан):
For<ISiteMapFactory>().Use<MySiteMapFactory>();
Обратите внимание, вам придется повторно реализовать конструктор RequestCacheableSiteMap так что все его зависимости передаются в объект так же, как и в реализации MySiteMapFactory.
Я не тестировал это решение, чтобы определить, есть ли какие-либо побочные эффекты, особенно если вы используете смешанный пакет маршрутов и URL-адресов.
Контроллер = "Заказчик/Заказ" не поддерживается. Если вам нужно добавить область, есть отдельная область поля = «Клиент» = «Заказы». Вероятно, поэтому ваше совпадение без учета регистра не работает. – NightOwl888
Вот и все! Извините, не видел этого. – Remy
Похоже, мне нужно добавить некоторую проверку недопустимых символов (например, «/») в поля контроллера, действия и области, чтобы этого не произошло. – NightOwl888