MVCSitemapProvider IsCurrentNode区分大小写?

时间:2013-11-22 17:03:40

标签: asp.net-mvc mvcsitemapprovider

看起来MVCSitemapProvider的IsCurrentNode属性区别于URL。

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 个答案:

答案 0 :(得分:1)

只要您使用URL Helper构建您的网址,使用.NET 4.5即可轻松实施小写网址:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.LowercaseUrls = true;
    ...
}

RouteCollection.LowercaseUrls

此外,通常最佳做法是使用小写URL来获得更好的SEO和缓存支持。

我怀疑之前没有报告过,因为大多数人使用@Html.ActionLink@Html.RouteLink@Html.Url帮助程序来构建他们的网址,这总是使他们的情况相同,所以比较将始终有效。

如果您将此报告为新问题@ GitHub,我会很感激,因此我们可以将其添加到工作队列中,因为我同意这是一个需要解决的错误。

潜在的问题有两个:

  1. SiteMap将URL存储为其中一个词典中的一个键,它会进行区分大小写的匹配。
  2. 在比较不区分大小写的路由之前,首先比较URL路径(区分大小写)。
  3. 这意味着如果所有网址都是小写生成的,那么问题就会消失。这是因为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。

相关问题