[转]学习Nop中Routes的使用

本文转自:http://www.cnblogs.com/miku/archive/2012/09/27/2706276.html

1. 映射路由

大型MVC项目为了扩展性,可维护性不能像一般项目在Global中RegisterRoutes的方法里面映射路由。

这里学习一下Nop是如何做的。

Global.cs  . 通过IOC容器取得IRoutePublisher实例

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("favicon.ico");
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            //register custom routes (plugins, etc)
            var routePublisher = EngineContext.Current.Resolve<IRoutePublisher>();
            routePublisher.RegisterRoutes(routes);

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                new[] { "Nop.Web.Controllers" }
            );
        }

IRoutePublisher.cs 只有一个方法

public interface IRoutePublisher
    {
        void RegisterRoutes(RouteCollection routeCollection);
    }

实现类

public class RoutePublisher : IRoutePublisher
    {
        private readonly ITypeFinder _typeFinder;

        public RoutePublisher(ITypeFinder typeFinder)
        {
            this._typeFinder = typeFinder;
        }

        public void RegisterRoutes(RouteCollection routes)
        {
            var routeProviderTypes = _typeFinder.FindClassesOfType<IRouteProvider>();
            var routeProviders = new List<IRouteProvider>();
            foreach (var providerType in routeProviderTypes)
            {
                var provider = Activator.CreateInstance(providerType) as IRouteProvider;
                routeProviders.Add(provider);
            }
            routeProviders = routeProviders.OrderByDescending(rp => rp.Priority).ToList();
            routeProviders.ForEach(rp => rp.RegisterRoutes(routes));
        }
    }

ITypeFinder之前已经介绍过。点击查看 ,搜索项目中所有的IRouteProvider实现。然后根据优先级排序,最后调用RegisterRoutes依次映射

竟然有这么多,大多都在Plugin程序集中为插件映射路由。随便打开一个。

public partial class RouteProvider : IRouteProvider
    {
        public void RegisterRoutes(RouteCollection routes)
        {
            routes.MapRoute("Plugin.Payments.AuthorizeNet.Configure",
                 "Plugins/PaymentAuthorizeNet/Configure",
                 new { controller = "PaymentAuthorizeNet", action = "Configure" },
                 new[] { "Nop.Plugin.Payments.AuthorizeNet.Controllers" }
            );

            routes.MapRoute("Plugin.Payments.AuthorizeNet.PaymentInfo",
                 "Plugins/PaymentAuthorizeNet/PaymentInfo",
                 new { controller = "PaymentAuthorizeNet", action = "PaymentInfo" },
                 new[] { "Nop.Plugin.Payments.AuthorizeNet.Controllers" }
            );
        }
        public int Priority
        {
            get
            {
                return 0;
            }
        }
    }

好处不用说了。模块化方便复用。

2.自定义Route

LocalizedRoute.cs

using System.Web;
using System.Web.Routing;
using Nop.Core.Data;
using Nop.Core.Domain.Localization;
using Nop.Core.Infrastructure;

namespace Nop.Web.Framework.Localization
{
    /// <summary>
    /// Provides properties and methods for defining a localized route, and for getting information about the localized route.
    /// </summary>
    public class LocalizedRoute : Route
    {
        #region Fields

        private bool? _seoFriendlyUrlsForLanguagesEnabled;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern and handler class.
        /// </summary>
        /// <param name="url">The URL pattern for the route.</param>
        /// <param name="routeHandler">The object that processes requests for the route.</param>
        public LocalizedRoute(string url, IRouteHandler routeHandler)
            : base(url, routeHandler)
        {
        }

        /// <summary>
        /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern, handler class and default parameter values.
        /// </summary>
        /// <param name="url">The URL pattern for the route.</param>
        /// <param name="defaults">The values to use if the URL does not contain all the parameters.</param>
        /// <param name="routeHandler">The object that processes requests for the route.</param>
        public LocalizedRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
            : base(url, defaults, routeHandler)
        {
        }

        /// <summary>
        /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern, handler class, default parameter values and constraints.
        /// </summary>
        /// <param name="url">The URL pattern for the route.</param>
        /// <param name="defaults">The values to use if the URL does not contain all the parameters.</param>
        /// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param>
        /// <param name="routeHandler">The object that processes requests for the route.</param>
        public LocalizedRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
            : base(url, defaults, constraints, routeHandler)
        {
        }

        /// <summary>
        /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern, handler class, default parameter values,
        /// constraints,and custom values.
        /// </summary>
        /// <param name="url">The URL pattern for the route.</param>
        /// <param name="defaults">The values to use if the URL does not contain all the parameters.</param>
        /// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param>
        /// <param name="dataTokens">Custom values that are passed to the route handler, but which are not used to determine whether the route matches a specific URL pattern. The route handler might need these values to process the request.</param>
        /// <param name="routeHandler">The object that processes requests for the route.</param>
        public LocalizedRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
            : base(url, defaults, constraints, dataTokens, routeHandler)
        {
        }

        #endregion

        #region Methods

        /// <summary>
        /// Returns information about the requested route.
        /// </summary>
        /// <param name="httpContext">An object that encapsulates information about the HTTP request.</param>
        /// <returns>
        /// An object that contains the values from the route definition.
        /// </returns>
        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            if (DataSettingsHelper.DatabaseIsInstalled() && this.SeoFriendlyUrlsForLanguagesEnabled)
            {
                string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
                string applicationPath = httpContext.Request.ApplicationPath;
                if (virtualPath.IsLocalizedUrl(applicationPath, false))
                {
                    //In ASP.NET Development Server, an URL like "http://localhost/Blog.aspx/Categories/BabyFrog" will return
                    //"~/Blog.aspx/Categories/BabyFrog" as AppRelativeCurrentExecutionFilePath.
                    //However, in II6, the AppRelativeCurrentExecutionFilePath is "~/Blog.aspx"
                    //It seems that IIS6 think we‘re process Blog.aspx page.
                    //So, I‘ll use RawUrl to re-create an AppRelativeCurrentExecutionFilePath like ASP.NET Development Server.

                    //Question: should we do path rewriting right here?
                    string rawUrl = httpContext.Request.RawUrl;
                    var newVirtualPath = rawUrl.RemoveLocalizedPathFromRawUrl(applicationPath);
                    if (string.IsNullOrEmpty(newVirtualPath))
                        newVirtualPath = "/";
                    newVirtualPath = newVirtualPath.RemoveApplicationPathFromRawUrl(applicationPath);
                    newVirtualPath = "~" + newVirtualPath;
                    httpContext.RewritePath(newVirtualPath, true);
                }
            }
            RouteData data = base.GetRouteData(httpContext);
            return data;
        }

        /// <summary>
        /// Returns information about the URL that is associated with the route.
        /// </summary>
        /// <param name="requestContext">An object that encapsulates information about the requested route.</param>
        /// <param name="values">An object that contains the parameters for a route.</param>
        /// <returns>
        /// An object that contains information about the URL that is associated with the route.
        /// </returns>
        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            VirtualPathData data = base.GetVirtualPath(requestContext, values);

            if (DataSettingsHelper.DatabaseIsInstalled() && this.SeoFriendlyUrlsForLanguagesEnabled)
            {
                if (data != null)
                {
                    string rawUrl = requestContext.HttpContext.Request.RawUrl;
                    string applicationPath = requestContext.HttpContext.Request.ApplicationPath;
                    if (rawUrl.IsLocalizedUrl(applicationPath, true))
                    {
                        data.VirtualPath = string.Concat(rawUrl.GetLanguageSeoCodeFromUrl(applicationPath, true), "/",
                                                         data.VirtualPath);
                    }
                }
            }
            return data;
        }

        public virtual void ClearSeoFriendlyUrlsCachedValue()
        {
            _seoFriendlyUrlsForLanguagesEnabled = null;
        }

        #endregion

        #region Properties

        protected bool SeoFriendlyUrlsForLanguagesEnabled
        {
            get
            {
                if (!_seoFriendlyUrlsForLanguagesEnabled.HasValue)
                    _seoFriendlyUrlsForLanguagesEnabled = EngineContext.Current.Resolve<LocalizationSettings>().SeoFriendlyUrlsForLanguagesEnabled;

                return _seoFriendlyUrlsForLanguagesEnabled.Value;
            }
        }

        #endregion
    }
}

继承Route并且实现GetRouteData, GetVirtualPath这2个方法。从返回的参数可以知道一个是解析URL,一个是生成URL。是一个双向的过程。

注意 if (DataSettingsHelper.DatabaseIsInstalled() && this.SeoFriendlyUrlsForLanguagesEnabled)  这个判断条件,启用了地区化友好SEO。才进行URL转换。

简单说就是 http://localhost:2619/en  《=》http://localhost:2619/  的双向转换。 en,ch之类的就是网站设置的语言了。

2。MVC路由测试

先看单元测试

映射路由

new Nop.Web.Infrastructure.RouteProvider().RegisterRoutes(RouteTable.Routes);

[Test]
        public void Boards_routes()
        {
            "~/boards/".ShouldMapTo<BoardsController>(c => c.Index());
            //TODO add support for optional parameters in ‘ShouldMapTo‘ method (such as in ~/boards/activediscussions/ or ~/boards/topic/11/). The same is about issue is in the other route test methods
            //"~/boards/activediscussions/".ShouldMapTo<BoardsController>(c => c.ActiveDiscussions(0));
            //"~/boards/activediscussionsrss/".ShouldMapTo<BoardsController>(c => c.ActiveDiscussionsRss(0));
            "~/boards/postedit/1".ShouldMapTo<BoardsController>(c => c.PostEdit(1));
            "~/boards/postdelete/2".ShouldMapTo<BoardsController>(c => c.PostDelete(2));
            "~/boards/postcreate/3".ShouldMapTo<BoardsController>(c => c.PostCreate(3, null));
            "~/boards/postcreate/4/5".ShouldMapTo<BoardsController>(c => c.PostCreate(4, 5));
            "~/boards/topicedit/6".ShouldMapTo<BoardsController>(c => c.TopicEdit(6));
            "~/boards/topicdelete/7".ShouldMapTo<BoardsController>(c => c.TopicDelete(7));
            "~/boards/topiccreate/8".ShouldMapTo<BoardsController>(c => c.TopicCreate(8));
            "~/boards/topicmove/9".ShouldMapTo<BoardsController>(c => c.TopicMove(9));
            "~/boards/topicwatch/10".ShouldMapTo<BoardsController>(c => c.TopicWatch(10));
            //"~/boards/topic/11/".ShouldMapTo<BoardsController>(c => c.Topic(11, 1));
            //"~/boards/topic/11/test-topic-slug".ShouldMapTo<BoardsController>(c => c.Topic(11, 1));
            "~/boards/topic/11/test-topic-slug/page/2".ShouldMapTo<BoardsController>(c => c.Topic(11, 2));
            "~/boards/forumwatch/12".ShouldMapTo<BoardsController>(c => c.ForumWatch(12));
            "~/boards/forumrss/13".ShouldMapTo<BoardsController>(c => c.ForumRss(13));
            //"~/boards/forum/14/".ShouldMapTo<BoardsController>(c => c.Forum(14, 1));
            //"~/boards/forum/14/test-forum-slug".ShouldMapTo<BoardsController>(c => c.Forum(14, 1));
            "~/boards/forum/14/test-forum-slug/page/2".ShouldMapTo<BoardsController>(c => c.Forum(14, 2));
            "~/boards/forumgroup/15/".ShouldMapTo<BoardsController>(c => c.ForumGroup(15));
            "~/boards/forumgroup/15/test-forumgroup-slug/".ShouldMapTo<BoardsController>(c => c.ForumGroup(15));
            //"~/boards/search/".ShouldMapTo<BoardsController>(c => c.Search(null, null, null, null, null, 1));
        }

看来这里的代码。膜拜作者吧。。之前一直不知道如何测试Route,最多是装个Routedebug什么的

接下来进行解析匹配

/// <summary>
        /// Asserts that the route matches the expression specified.  Checks controller, action, and any method arguments
        /// into the action as route values.
        /// </summary>
        /// <typeparam name="TController">The controller.</typeparam>
        /// <param name="routeData">The routeData to check</param>
        /// <param name="action">The action to call on TController.</param>
        public static RouteData ShouldMapTo<TController>(this RouteData routeData, Expression<Func<TController, ActionResult>> action)
            where TController : Controller
        {
            routeData.ShouldNotBeNull("The URL did not match any route");

            //check controller
            routeData.ShouldMapTo<TController>();

            //check action
            var methodCall = (MethodCallExpression)action.Body;
            string actualAction = routeData.Values.GetValue("action").ToString();

            string expectedAction = methodCall.Method.ActionName();
            actualAction.AssertSameStringAs(expectedAction);

            //check parameters
            for (int i = 0; i < methodCall.Arguments.Count; i++)
            {
                ParameterInfo param = methodCall.Method.GetParameters()[i];
                bool isReferenceType = !param.ParameterType.IsValueType;
                bool isNullable = isReferenceType ||
                    (param.ParameterType.UnderlyingSystemType.IsGenericType && param.ParameterType.UnderlyingSystemType.GetGenericTypeDefinition() == typeof(Nullable<>));

                string controllerParameterName = param.Name;
                bool routeDataContainsValueForParameterName = routeData.Values.ContainsKey(controllerParameterName);
                object actualValue = routeData.Values.GetValue(controllerParameterName);
                object expectedValue = null;
                Expression expressionToEvaluate = methodCall.Arguments[i];

                // If the parameter is nullable and the expression is a Convert UnaryExpression,
                // we actually want to test against the value of the expression‘s operand.
                if (expressionToEvaluate.NodeType == ExpressionType.Convert
                    && expressionToEvaluate is UnaryExpression)
                {
                    expressionToEvaluate = ((UnaryExpression)expressionToEvaluate).Operand;
                }

                switch (expressionToEvaluate.NodeType)
                {
                    case ExpressionType.Constant:
                        expectedValue = ((ConstantExpression)expressionToEvaluate).Value;
                        break;

                    case ExpressionType.New:
                    case ExpressionType.MemberAccess:
                        expectedValue = Expression.Lambda(expressionToEvaluate).Compile().DynamicInvoke();
                        break;
                }

                if (isNullable && (string)actualValue == String.Empty && expectedValue == null)
                {
                    // The parameter is nullable so an expected value of ‘‘ is equivalent to null;
                    continue;
                }

                // HACK: this is only sufficient while System.Web.Mvc.UrlParameter has only a single value.
                if (actualValue == UrlParameter.Optional ||
                    (actualValue != null && actualValue.ToString().Equals("System.Web.Mvc.UrlParameter")))
                {
                    actualValue = null;
                }

                if (expectedValue is DateTime)
                {
                    actualValue = Convert.ToDateTime(actualValue);
                }
                else
                {
                    expectedValue = (expectedValue == null ? expectedValue : expectedValue.ToString());
                }

                string errorMsgFmt = "Value for parameter ‘{0}‘ did not match: expected ‘{1}‘ but was ‘{2}‘";
                if (routeDataContainsValueForParameterName)
                {
                    errorMsgFmt += ".";
                }
                else
                {
                    errorMsgFmt += "; no value found in the route context action parameter named ‘{0}‘ - does your matching route contain a token called ‘{0}‘?";
                }
                actualValue.ShouldEqual(expectedValue, String.Format(errorMsgFmt, controllerParameterName, expectedValue, actualValue));
            }

            return routeData;
        }

这里又看到了表达式树强大的地方。关于表达式树具体使用请搜索博客园,  这里通过routeData和解析action来判断请求和action是否一致。

总结:通过学习Nop中路由的一些使用。掌握了一些很有用的Route的使用。这些模块今后也能根据需要加入到自己的网站中。

参考:

两篇关于自定义路由:http://www.cnblogs.com/john-connor/archive/2012/05/03/2478821.html

http://www.cnblogs.com/ldp615/archive/2011/12/05/asp-net-mvc-elegant-route.html

时间: 2024-11-07 02:33:19

[转]学习Nop中Routes的使用的相关文章

Nop中的Cache浅析

Nop中定义了ICacheManger接口,它有几个实现,其中MemoryCacheManager是内存缓存的一个实现. MemoryCacheManager: using System; using System.Collections.Generic; using System.Runtime.Caching; using System.Text.RegularExpressions; namespace Nop.Core.Caching { /// <summary> /// Repre

如何理解并学习javascript中的面向对象(OOP) [转]

如果你想让你的javascript代码变得更加优美,性能更加卓越.或者,你想像jQuery的作者一样,写出属于自己优秀的类库(哪怕是基于 jquery的插件).那么,你请务必要学习javascript面向对象,否则你无法更灵活的使用javascript这门语言. 什么事闭包?到底什么是原型?(知道闭包和原型的,就算得上是javascript的高手了.但真正能够理解,并且灵活运用的人并不多)到底该如何学习javascript中的面向对象呢?在javascript这么语言正如日中天,相信不少人正在为

Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍

1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过start方法启动线程--->线程变为可运行可执行状态,然后通过数据产生共享,线程产生互斥---->线程状态变为阻塞状态---->阻塞状态想打开的话可以调用notify方法. 这里Java5中提供了封装好的类,可以直接调用然后构造阻塞状态,以保证数据的原子性. 2.如何实现? 主要是实现Blo

开源学习--SlideExpandableListView中的列表项动画实现框架分析

前面的话 开源项目Android-SlideExpandableListView是一个简单的介绍列表项动画展示的小型项目,分析这个项目可以对自定义框架及列表类动画实现有个比较清晰的认识,工作中中时常根据需求扩展定义自己的适配器,虽然具体需求不同,但架构类似,本文把最近关于该开源项目的研究心得整理分享,共同学习~ 项目简介 github地址https://github.com/tjerkw/Android-SlideExpandableListView 这是个入门级的列表项动画展示框架,实现效果如

浅谈设计模式的学习(中)

在<浅谈设计模式的学习(上)>中我说到了设计模式的基石-----抽象思维.为什么需要抽象思维呢?因为越抽象就越不容易出错,就像有些领导人说话:坚持改革开放.但怎么算坚持改革开放呢,没有具体的标准,因事而异,所以就不容易违背这个坚持改革开放的原则了. 3.学习设计模式,要保持抽象的思维     什么是抽象思维呢?真的不好说,抽象的东西往往难以说明白,听了也难以搞明白,还是通过具体的例子来说吧 有这么一个学生请假的场景,如果请假时间一天以内则有班长批准就可以了,三天以内则需要老师批准,超过三天就得

我在学习编程中犯的两个最大错误

我在学习编程中犯的两个最大错误 投递人 itwriter 发布于 2012-09-10 09:24 评论(13) 有2616人阅读  原文链接  [收藏]  « » 英文原文:Suneel Chakravorty 一年前,我刚从大学毕业并且决定踏入社会.我想出了很多初始的想法并将他们实现,但我不懂编程. 听从 Yipit 联合创始人 Vin Vacant 的建议之后,我开始自学编程. 现在我终于学了足够多的知识来自己实现产品原型,一路走来,非常坎坷.如果你在跟我同样的路上,我希望你能避免犯跟我同

学习JSP中如何发送一个动态图像

武汉java培训学习JSP中如何发送一个动态图像,你是否曾经想过从jsp页面(或者servlet)中发送动态产生的图像?这篇技巧告诉你如何做.要运行这里的代码,你需要一个Tomcat或者其他支持JSP 1.1的web服务器. 当一个web页面带有imagejpeg (或者其他的图像格式)的MIME类型被发送时,你的浏览器将那个返回结果当作一个图像,然后浏览器显示图像,作为页面的一部分或者完全作为图像自身.要为你的jsp页面设置MIME类型,你需要设置页面的contentType属性:<%@ pa

AngularJS学习--- AngularJS中数据双向绑定(two-way data-binding) orderBy step4

1.切换工作目录 git checkout step-4 #切换分支,切换到第4步 npm start #启动项目 2.代码 app/index.html Search: <input ng-model="query"> Sort by: <select ng-model="orderProp"> <option value="name">Alphabetical</option> <opti

再次学习javascript中的参数传递

javascript中的所有函数的参数传递都是按照值传递的,做了下面测试: function addTen(num){ num +=10; return num; } var count = 20; var result = addTen(count); alert(cont); //20 alert(result); //30 好吧,上面只是做了基本类型的传递,再做个引用类型的传递看看: function setName(obj){ obj.name="Mark"; } var pe