2013-11-22 4 views
0

Похоже, что свойство IsCurrentNode MVCSitemapProvider чувствительно к изменению URL-адреса.MVCSitemapProvider IsCurrentNode чувствителен к регистру?

E.g.

https://localhost/MvcApplication1/customer/exportdata (no match) 
https://localhost/MvcApplication1/Customer/ExportData (works) 

Поскольку случай с URL-адресами немного сложно обеспечить, есть ли способ обойти это?

Это как моя карта сайта в настоящее время выглядит следующим образом:

<mvcSiteMapNode title="Home" controller="Home" action="Index" clickable="false"> 
    <mvcSiteMapNode title="Meine Aufträge" controller="Home" action="Index"> 
     <mvcSiteMapNode title="Übersicht" controller="Customer/Orders" action="Index"/> 
     <mvcSiteMapNode title="Rechnungen" controller="Customer/Bills" action="Index" roles="CompanyAdmin"/> 
     <mvcSiteMapNode title="Export" controller="Customer/ExportData" action="Index"/> 
    </mvcSiteMapNode> 
    <mvcSiteMapNode title="Firmenadministration" controller="Home" action="Index"> 
     <mvcSiteMapNode title="Übersicht" controller="AllOrders" action="About"/> 
    </mvcSiteMapNode> 
    <mvcSiteMapNode title="Freelancer" url="http://www.myblog1.com" > 
     <mvcSiteMapNode title="Übersicht" url="http://www.myblog2.com" /> 
     <mvcSiteMapNode title="Rechnungen" url="http://www.myblog3.com" /> 
    </mvcSiteMapNode> 
</mvcSiteMapNode> 

Части это только данные испытаний.

+1

Контроллер = "Заказчик/Заказ" не поддерживается. Если вам нужно добавить область, есть отдельная область поля = «Клиент» = «Заказы». Вероятно, поэтому ваше совпадение без учета регистра не работает. – NightOwl888

+0

Вот и все! Извините, не видел этого. – Remy

+0

Похоже, мне нужно добавить некоторую проверку недопустимых символов (например, «/») в поля контроллера, действия и области, чтобы этого не произошло. – NightOwl888

ответ

1

Обеспечение строчных 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, поэтому мы можем добавить это в рабочую очередь, так как я согласен, что это ошибка, которую необходимо решить.

Основная проблема имеет два аспекта:

  1. карта сайта сохраняет URL-адреса в качестве ключа в одном из словарей, который делает случай чувствительный матч.
  2. Путь к 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-адресов.

+1

Хорошо, сделаю. Но с нижестоящим URL-адресом SiteMap все равно не будет соответствовать им, не так ли? Так или иначе, я могу это исправить? Мы используем ActionLink и прочее, но люди также приходят непосредственно к URL-адресам, а затем они могут не всегда быть правы. – Remy

+0

Я обновил свой ответ. – NightOwl888

+0

BTW. Вы можете использовать переписывание URL-адресов, чтобы обеспечить перенаправление пользователей на нижний адрес. Http://ruslany.net/2009/04/10-url-rewriting-tips-and-tricks/ – NightOwl888

Смежные вопросы