В .NET 4.5 довольно легко ввести URL-адреса в нижнем регистре, если вы используете помощник URL для создания своих URL-адресов:
public static void RegisterRoutes(RouteCollection routes)
{
routes.LowercaseUrls = true;
...
}
RouteCollection.LowercaseUrls
Кроме того, как правило, рекомендуется использовать URL-адреса в нижнем регистре для улучшения SEO и поддержки кэширования.
Я подозреваю, что об этом раньше не сообщалось, потому что большинство людей используют помощники @Html.ActionLink
, @Html.RouteLink
или @Html.Url
для создания своих URL-адресов, что всегда делает их одинаковыми, поэтому сравнение всегда будет работать.
Буду признателен, если вы сообщите об этом как о новой проблеме @ GitHub, чтобы мы можете добавить это в рабочую очередь, так как я согласен, что это ошибка, которую необходимо устранить.
Основная проблема двояка:
- SiteMap хранит 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 и внедряет его с помощью внешнего внедрения зависимостей. Это решение не будет работать при использовании внутреннего контейнера внедрения зависимостей.
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
}
Затем введите что-то вроде этого в модуль DI MvcSiteMapProvider (показан пример StructureMap):
For<ISiteMapFactory>().Use<MySiteMapFactory>();
Обратите внимание, что вам придется повторно реализовать конструктор RequestCacheableSiteMap, чтобы все его зависимости передавались в объект точно так же, как в реализации MySiteMapFactory.
Я не тестировал это решение, чтобы определить, есть ли какие-либо побочные эффекты, особенно если вы используете смешанный набор маршрутов и URL-адресов.
person
NightOwl888
schedule
23.11.2013