刚开始研究nop的同学要找到里面一个Url对应Controller相关代码,可能会有点晕。因为NopCommerce为了对seo友好,对其Url做了一些处理,自定义了路由规则,同时为了支持插件机制,加了一些自己的类进行扩展。本文就来分析一个NopCommerce路由相关源码设计思路,同样我们也先来看看相关的类图:
上面就是NopCommerce路由相关功能主要的类、接口及关系。有以下类:
1、mvcApplication
2、IRoutePublisher、RoutePublisher
3、IRouteProvider、RouteProvider、GenericUrlRouteProvider
其中RoutePublisher是用来发布RouteProvider、GenericUrlRouteProvider里面配置的路由规则的,二者都有一个抽象的接口。接下来我们就来看看这些类或接口中代码是如何实现的:
Nop.Web.MvcApplication
- public static void RegisterRoutes(RouteCollection routes)
- {
- routes.IgnoreRoute("favicon.ico");
- routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
- //注册自定义的路由规则及插件相关路由
- 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" }
- );
- }
EngineContext.Current.Resolve<IRoutePublisher>()这名代码表示,从Ioc容器Autofac中获取接口IRoutePublisher所依赖的具体实现类。如果对Nop的依赖注入原理还是很了解,可以参考我之前的文章NopCommerce源码架构详解--Autofac依赖注入分析。
在Nop.Web.Framework.DependencyRegistrar中有以下代码:
- builder.RegisterType<RoutePublisher>().As<IRoutePublisher>().SingleInstance();
Nop.Web.Framework.Mvc.Routes.RoutePublisher
我们找到这个RoutePublisher类(Nop.Web.Framework.Mvc.Routes.RoutePublisher)其中关键的代码如下:
- public virtual void RegisterRoutes(RouteCollection routes)
- {
- //通过typeFinder找出所有(包括插件)实现了接口IRouteProvider相关的类型
- var routeProviderTypes = typeFinder.FindClassesOfType<IRouteProvider>();
- var routeProviders = new List<IRouteProvider>();
- foreach (var providerType in routeProviderTypes)
- {
- //Ignore not installed plugins
- var plugin = FindPlugin(providerType);
- if (plugin != null && !plugin.Installed)
- continue;
- //采用反射动态创建IRouteProvider的具体类的实例
- var provider = Activator.CreateInstance(providerType) as IRouteProvider;
- routeProviders.Add(provider);
- }
- routeProviders = routeProviders.OrderByDescending(rp => rp.Priority).ToList();
- //依次调用RouteProvider的RegisterRoutes方法,注册路由规则
- routeProviders.ForEach(rp => rp.RegisterRoutes(routes));
- }
Nop.Web.Infrastructure.RouteProvider
现在我们来看看一个具体的RouteProvider里面都有些什么东东。在项目Nop.Web根目录下面有一个文件夹Infrastructure,里面有一个RouteProvider类,如下图:
- public void RegisterRoutes(RouteCollection routes)
- {
- //We reordered our routes so the most used ones are on top. It can improve performance.
- //home page
- routes.MapLocalizedRoute("HomePage",
- "",
- new { controller = "Home", action = "Index" },
- new[] { "Nop.Web.Controllers" });
- //widgets
- //we have this route for performance optimization because named routes are MUCH faster than usual Html.Action(...)
- //and this route is highly used
- routes.MapRoute("WidgetsByZone",
- "widgetsbyzone/",
- new { controller = "Widget", action = "WidgetsByZone" },
- new[] { "Nop.Web.Controllers" });
- //login
- routes.MapLocalizedRoute("Login",
- "login/",
- new { controller = "Customer", action = "Login" },
- new[] { "Nop.Web.Controllers" });
- //register
- routes.MapLocalizedRoute("Register",
- "register/",
- new { controller = "Customer", action = "Register" },
- new[] { "Nop.Web.Controllers" });
- //logout
- routes.MapLocalizedRoute("Logout",
- "logout/",
- new { controller = "Customer", action = "Logout" },
- new[] { "Nop.Web.Controllers" });
- //shopping cart
- routes.MapLocalizedRoute("ShoppingCart",
- "cart/",
- new { controller = "ShoppingCart", action = "Cart" },
- new[] { "Nop.Web.Controllers" });
- //wishlist
- routes.MapLocalizedRoute("Wishlist",
- "wishlist/{customerGuid}",
- new { controller = "ShoppingCart", action = "Wishlist", customerGuid = UrlParameter.Optional },
- new[] { "Nop.Web.Controllers" });
- //customer
- routes.MapLocalizedRoute("CustomerInfo",
- "customer/info",
- new { controller = "Customer", action = "Info" },
- new[] { "Nop.Web.Controllers" });
- //....省略剩余代码
- }
RouteProvider的方法RegisterRoutes就是真正自定义路由规则。我们如果要找一个Url对应的Controller就要先在这里面查找一下,才好定位到是哪一个Controller。至于插件的路由我接下来会用专门一篇文章来介绍Nop的插件机制。
Nop.Web.Infrastructure.GenericUrlRouteProvider
GenericUrlRouteProvider和Nop.Web.Infrastructure.RouteProvider是相同级别的都是实现了接口IRouteProvider,区别GenericUrlRouteProvider定义的一般公用的Url规则。如下代码:
可以看到上面可以看到上面定义了商品列表、商品详情及新闻等相关的Url规则。
- public partial class GenericUrlRouteProvider : IRouteProvider
- {
- public void RegisterRoutes(RouteCollection routes)
- {
- //generic URLs
- routes.MapGenericPathRoute("GenericUrl",
- "{generic_se_name}",
- new {controller = "Common", action = "GenericUrl"},
- new[] {"Nop.Web.Controllers"});
- //define this routes to use in UI views (in case if you want to customize some of them later)
- routes.MapLocalizedRoute("Product",
- "{SeName}",
- new { controller = "Product", action = "ProductDetails" },
- new[] {"Nop.Web.Controllers"});
- routes.MapLocalizedRoute("Category",
- "{SeName}",
- new { controller = "Catalog", action = "Category" },
- new[] { "Nop.Web.Controllers" });
- routes.MapLocalizedRoute("Manufacturer",
- "{SeName}",
- new { controller = "Catalog", action = "Manufacturer" },
- new[] { "Nop.Web.Controllers" });
- routes.MapLocalizedRoute("Vendor",
- "{SeName}",
- new { controller = "Catalog", action = "Vendor" },
- new[] { "Nop.Web.Controllers" });
- routes.MapLocalizedRoute("NewsItem",
- "{SeName}",
- new { controller = "News", action = "NewsItem" },
- new[] { "Nop.Web.Controllers" });
- routes.MapLocalizedRoute("BlogPost",
- "{SeName}",
- new { controller = "Blog", action = "BlogPost" },
- new[] { "Nop.Web.Controllers" });
- routes.MapLocalizedRoute("Topic",
- "{SeName}",
- new { controller = "Topic", action = "TopicDetails" },
- new[] { "Nop.Web.Controllers" });
- }
- public int Priority
- {
- get
- {
- //it should be the last route
- //we do not set it to -int.MaxValue so it could be overriden (if required)
- return -1000000;
- }
- }
- }