nopCommerce 3.9 大波浪系列 之 路由注册
在Global.asax,Application_Start()方法中会进行路由注册,代码如下。
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" } ); }
我们会发现调用了IRutePublisher接口,该接口由Nop.Web.Framework.Mvc.Routes.RoutePublisher类实现。
并通过RegisterRoutes(RouteCollection routes)方法进行路由注册。代码如下:
/// <summary> /// Register routes /// </summary> /// <param name="routes">Routes</param> public virtual void RegisterRoutes(RouteCollection routes) { //ITypeFinder接口获取所有IRouteProvider接口实现类的Type对象 var routeProviderTypes = typeFinder.FindClassesOfType<IRouteProvider>(); var routeProviders = new List<IRouteProvider>(); foreach (var providerType in routeProviderTypes) { //Ignore not installed plugins var plugin = FindPlugin(providerType);//PluginManager.ReferencedPlugins中查找该Type对象 if (plugin != null && !plugin.Installed)//插件不为空未安装则跳出本次循环 continue; var provider = Activator.CreateInstance(providerType) as IRouteProvider;//实例化IRouteProvider接口对象 routeProviders.Add(provider); } routeProviders = routeProviders.OrderByDescending(rp => rp.Priority).ToList();//排序 routeProviders.ForEach(rp => rp.RegisterRoutes(routes));//遍历并调用RegisterRoutes(routes)进行路由注册 }
该方法做了如下工作:
第一步:通过ITyperFinder接口找到所有实现了IRouteProvider接口的类类型。保存在routeProviderTypes集合中
第二步:遍历routeProviderTypes集合,通过PluginManager类找到未安装IRouteProvider类类型。并从routeProviderTypes集合中排除掉。
第三步:实例化IRouteProvider实现类。
第四部:调用IRouteProvider实现类RegisterRoutes(routes)方法进行路由注册。
下图为主要接口之间的调用关系
通过上述分析nop路由注册主要是通过IRouteProvider接口实现的,现在我们看看项目中实现该接口的类都有哪些。
下图红色是在Nop.Web项目中,其他都是在nop插件中。
接下来我们看下路由的注册顺序如下图,我们看到最先匹配的Nop.Web.Infrastructure.RouteProvider中的路由注册。
启用支持多语言的SEO友好链接
nop支持SEO友好链接,管理后台->设置管理->综合设置->启用支持多语言的SEO友好链接 选中就可以支持了。
启用后URL格式为: http://www.yourStore.com/en/ 或 http://www.yourStore.com/zh/ (SEO比较友好)
那该功能怎么实现的呢?接下来我们看看nop是如何通过路由扩展实现的。
(Nop.Web.Framework.WebWorkContext在多语言中也会用到,这里先不讲它,我们只说路由)
我们先看下Nop.Web.Infrastructure.RouteProvider中的路由注册代码
using System.Web.Mvc; using System.Web.Routing; using Nop.Web.Framework.Localization; using Nop.Web.Framework.Mvc.Routes; namespace Nop.Web.Infrastructure { public partial class RouteProvider : IRouteProvider { 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" }); //estimate shipping routes.MapLocalizedRoute("EstimateShipping", "cart/estimateshipping", new {controller = "ShoppingCart", action = "GetEstimateShipping"}, new[] {"Nop.Web.Controllers"}); //wishlist routes.MapLocalizedRoute("Wishlist", "wishlist/{customerGuid}", new { controller = "ShoppingCart", action = "Wishlist", customerGuid = UrlParameter.Optional }, new[] { "Nop.Web.Controllers" }); //customer account links routes.MapLocalizedRoute("CustomerInfo", "customer/info", new { controller = "Customer", action = "Info" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerAddresses", "customer/addresses", new { controller = "Customer", action = "Addresses" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerOrders", "order/history", new { controller = "Order", action = "CustomerOrders" }, new[] { "Nop.Web.Controllers" }); //contact us routes.MapLocalizedRoute("ContactUs", "contactus", new { controller = "Common", action = "ContactUs" }, new[] { "Nop.Web.Controllers" }); //sitemap routes.MapLocalizedRoute("Sitemap", "sitemap", new { controller = "Common", action = "Sitemap" }, new[] { "Nop.Web.Controllers" }); //product search routes.MapLocalizedRoute("ProductSearch", "search/", new { controller = "Catalog", action = "Search" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("ProductSearchAutoComplete", "catalog/searchtermautocomplete", new { controller = "Catalog", action = "SearchTermAutoComplete" }, new[] { "Nop.Web.Controllers" }); //change currency (AJAX link) routes.MapLocalizedRoute("ChangeCurrency", "changecurrency/{customercurrency}", new { controller = "Common", action = "SetCurrency" }, new { customercurrency = @"\d+" }, new[] { "Nop.Web.Controllers" }); //change language (AJAX link) routes.MapLocalizedRoute("ChangeLanguage", "changelanguage/{langid}", new { controller = "Common", action = "SetLanguage" }, new { langid = @"\d+" }, new[] { "Nop.Web.Controllers" }); //change tax (AJAX link) routes.MapLocalizedRoute("ChangeTaxType", "changetaxtype/{customertaxtype}", new { controller = "Common", action = "SetTaxType" }, new { customertaxtype = @"\d+" }, new[] { "Nop.Web.Controllers" }); //recently viewed products routes.MapLocalizedRoute("RecentlyViewedProducts", "recentlyviewedproducts/", new { controller = "Product", action = "RecentlyViewedProducts" }, new[] { "Nop.Web.Controllers" }); //new products routes.MapLocalizedRoute("NewProducts", "newproducts/", new { controller = "Product", action = "NewProducts" }, new[] { "Nop.Web.Controllers" }); //blog routes.MapLocalizedRoute("Blog", "blog", new { controller = "Blog", action = "List" }, new[] { "Nop.Web.Controllers" }); //news routes.MapLocalizedRoute("NewsArchive", "news", new { controller = "News", action = "List" }, new[] { "Nop.Web.Controllers" }); //forum routes.MapLocalizedRoute("Boards", "boards", new { controller = "Boards", action = "Index" }, new[] { "Nop.Web.Controllers" }); //compare products routes.MapLocalizedRoute("CompareProducts", "compareproducts/", new { controller = "Product", action = "CompareProducts" }, new[] { "Nop.Web.Controllers" }); //product tags routes.MapLocalizedRoute("ProductTagsAll", "producttag/all/", new { controller = "Catalog", action = "ProductTagsAll" }, new[] { "Nop.Web.Controllers" }); //manufacturers routes.MapLocalizedRoute("ManufacturerList", "manufacturer/all/", new { controller = "Catalog", action = "ManufacturerAll" }, new[] { "Nop.Web.Controllers" }); //vendors routes.MapLocalizedRoute("VendorList", "vendor/all/", new { controller = "Catalog", action = "VendorAll" }, new[] { "Nop.Web.Controllers" }); //add product to cart (without any attributes and options). used on catalog pages. routes.MapLocalizedRoute("AddProductToCart-Catalog", "addproducttocart/catalog/{productId}/{shoppingCartTypeId}/{quantity}", new { controller = "ShoppingCart", action = "AddProductToCart_Catalog" }, new { productId = @"\d+", shoppingCartTypeId = @"\d+", quantity = @"\d+" }, new[] { "Nop.Web.Controllers" }); //add product to cart (with attributes and options). used on the product details pages. routes.MapLocalizedRoute("AddProductToCart-Details", "addproducttocart/details/{productId}/{shoppingCartTypeId}", new { controller = "ShoppingCart", action = "AddProductToCart_Details" }, new { productId = @"\d+", shoppingCartTypeId = @"\d+" }, new[] { "Nop.Web.Controllers" }); //product tags routes.MapLocalizedRoute("ProductsByTag", "producttag/{productTagId}/{SeName}", new { controller = "Catalog", action = "ProductsByTag", SeName = UrlParameter.Optional }, new { productTagId = @"\d+" }, new[] { "Nop.Web.Controllers" }); //comparing products routes.MapLocalizedRoute("AddProductToCompare", "compareproducts/add/{productId}", new { controller = "Product", action = "AddProductToCompareList" }, new { productId = @"\d+" }, new[] { "Nop.Web.Controllers" }); //product email a friend routes.MapLocalizedRoute("ProductEmailAFriend", "productemailafriend/{productId}", new { controller = "Product", action = "ProductEmailAFriend" }, new { productId = @"\d+" }, new[] { "Nop.Web.Controllers" }); //reviews routes.MapLocalizedRoute("ProductReviews", "productreviews/{productId}", new { controller = "Product", action = "ProductReviews" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerProductReviews", "customer/productreviews", new { controller = "Product", action = "CustomerProductReviews" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerProductReviewsPaged", "customer/productreviews/page/{page}", new { controller = "Product", action = "CustomerProductReviews" }, new { page = @"\d+" }, new[] { "Nop.Web.Controllers" }); //back in stock notifications routes.MapLocalizedRoute("BackInStockSubscribePopup", "backinstocksubscribe/{productId}", new { controller = "BackInStockSubscription", action = "SubscribePopup" }, new { productId = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("BackInStockSubscribeSend", "backinstocksubscribesend/{productId}", new { controller = "BackInStockSubscription", action = "SubscribePopupPOST" }, new { productId = @"\d+" }, new[] { "Nop.Web.Controllers" }); //downloads routes.MapRoute("GetSampleDownload", "download/sample/{productid}", new { controller = "Download", action = "Sample" }, new { productid = @"\d+" }, new[] { "Nop.Web.Controllers" }); //checkout pages routes.MapLocalizedRoute("Checkout", "checkout/", new { controller = "Checkout", action = "Index" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CheckoutOnePage", "onepagecheckout/", new { controller = "Checkout", action = "OnePageCheckout" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CheckoutShippingAddress", "checkout/shippingaddress", new { controller = "Checkout", action = "ShippingAddress" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CheckoutSelectShippingAddress", "checkout/selectshippingaddress", new { controller = "Checkout", action = "SelectShippingAddress" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CheckoutBillingAddress", "checkout/billingaddress", new { controller = "Checkout", action = "BillingAddress" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CheckoutSelectBillingAddress", "checkout/selectbillingaddress", new { controller = "Checkout", action = "SelectBillingAddress" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CheckoutShippingMethod", "checkout/shippingmethod", new { controller = "Checkout", action = "ShippingMethod" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CheckoutPaymentMethod", "checkout/paymentmethod", new { controller = "Checkout", action = "PaymentMethod" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CheckoutPaymentInfo", "checkout/paymentinfo", new { controller = "Checkout", action = "PaymentInfo" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CheckoutConfirm", "checkout/confirm", new { controller = "Checkout", action = "Confirm" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CheckoutCompleted", "checkout/completed/{orderId}", new { controller = "Checkout", action = "Completed", orderId = UrlParameter.Optional }, new { orderId = @"\d+" }, new[] { "Nop.Web.Controllers" }); //subscribe newsletters routes.MapLocalizedRoute("SubscribeNewsletter", "subscribenewsletter", new { controller = "Newsletter", action = "SubscribeNewsletter" }, new[] { "Nop.Web.Controllers" }); //email wishlist routes.MapLocalizedRoute("EmailWishlist", "emailwishlist", new { controller = "ShoppingCart", action = "EmailWishlist" }, new[] { "Nop.Web.Controllers" }); //login page for checkout as guest routes.MapLocalizedRoute("LoginCheckoutAsGuest", "login/checkoutasguest", new { controller = "Customer", action = "Login", checkoutAsGuest = true }, new[] { "Nop.Web.Controllers" }); //register result page routes.MapLocalizedRoute("RegisterResult", "registerresult/{resultId}", new { controller = "Customer", action = "RegisterResult" }, new { resultId = @"\d+" }, new[] { "Nop.Web.Controllers" }); //check username availability routes.MapLocalizedRoute("CheckUsernameAvailability", "customer/checkusernameavailability", new { controller = "Customer", action = "CheckUsernameAvailability" }, new[] { "Nop.Web.Controllers" }); //passwordrecovery routes.MapLocalizedRoute("PasswordRecovery", "passwordrecovery", new { controller = "Customer", action = "PasswordRecovery" }, new[] { "Nop.Web.Controllers" }); //password recovery confirmation routes.MapLocalizedRoute("PasswordRecoveryConfirm", "passwordrecovery/confirm", new { controller = "Customer", action = "PasswordRecoveryConfirm" }, new[] { "Nop.Web.Controllers" }); //topics routes.MapLocalizedRoute("TopicPopup", "t-popup/{SystemName}", new { controller = "Topic", action = "TopicDetailsPopup" }, new[] { "Nop.Web.Controllers" }); //blog routes.MapLocalizedRoute("BlogByTag", "blog/tag/{tag}", new { controller = "Blog", action = "BlogByTag" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("BlogByMonth", "blog/month/{month}", new { controller = "Blog", action = "BlogByMonth" }, new[] { "Nop.Web.Controllers" }); //blog RSS routes.MapLocalizedRoute("BlogRSS", "blog/rss/{languageId}", new { controller = "Blog", action = "ListRss" }, new { languageId = @"\d+" }, new[] { "Nop.Web.Controllers" }); //news RSS routes.MapLocalizedRoute("NewsRSS", "news/rss/{languageId}", new { controller = "News", action = "ListRss" }, new { languageId = @"\d+" }, new[] { "Nop.Web.Controllers" }); //set review helpfulness (AJAX link) routes.MapRoute("SetProductReviewHelpfulness", "setproductreviewhelpfulness", new { controller = "Product", action = "SetProductReviewHelpfulness" }, new[] { "Nop.Web.Controllers" }); //customer account links routes.MapLocalizedRoute("CustomerReturnRequests", "returnrequest/history", new { controller = "ReturnRequest", action = "CustomerReturnRequests" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerDownloadableProducts", "customer/downloadableproducts", new { controller = "Customer", action = "DownloadableProducts" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerBackInStockSubscriptions", "backinstocksubscriptions/manage", new { controller = "BackInStockSubscription", action = "CustomerSubscriptions" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerBackInStockSubscriptionsPaged", "backinstocksubscriptions/manage/{page}", new { controller = "BackInStockSubscription", action = "CustomerSubscriptions", page = UrlParameter.Optional }, new { page = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerRewardPoints", "rewardpoints/history", new { controller = "Order", action = "CustomerRewardPoints" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerRewardPointsPaged", "rewardpoints/history/page/{page}", new { controller = "Order", action = "CustomerRewardPoints" }, new { page = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerChangePassword", "customer/changepassword", new { controller = "Customer", action = "ChangePassword" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerAvatar", "customer/avatar", new { controller = "Customer", action = "Avatar" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("AccountActivation", "customer/activation", new { controller = "Customer", action = "AccountActivation" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("EmailRevalidation", "customer/revalidateemail", new { controller = "Customer", action = "EmailRevalidation" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerForumSubscriptions", "boards/forumsubscriptions", new { controller = "Boards", action = "CustomerForumSubscriptions" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerForumSubscriptionsPaged", "boards/forumsubscriptions/{page}", new { controller = "Boards", action = "CustomerForumSubscriptions", page = UrlParameter.Optional }, new { page = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerAddressEdit", "customer/addressedit/{addressId}", new { controller = "Customer", action = "AddressEdit" }, new { addressId = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerAddressAdd", "customer/addressadd", new { controller = "Customer", action = "AddressAdd" }, new[] { "Nop.Web.Controllers" }); //customer profile page routes.MapLocalizedRoute("CustomerProfile", "profile/{id}", new { controller = "Profile", action = "Index" }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("CustomerProfilePaged", "profile/{id}/page/{page}", new { controller = "Profile", action = "Index" }, new { id = @"\d+", page = @"\d+" }, new[] { "Nop.Web.Controllers" }); //orders routes.MapLocalizedRoute("OrderDetails", "orderdetails/{orderId}", new { controller = "Order", action = "Details" }, new { orderId = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("ShipmentDetails", "orderdetails/shipment/{shipmentId}", new { controller = "Order", action = "ShipmentDetails" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("ReturnRequest", "returnrequest/{orderId}", new { controller = "ReturnRequest", action = "ReturnRequest" }, new { orderId = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("ReOrder", "reorder/{orderId}", new { controller = "Order", action = "ReOrder" }, new { orderId = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("GetOrderPdfInvoice", "orderdetails/pdf/{orderId}", new { controller = "Order", action = "GetPdfInvoice" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("PrintOrderDetails", "orderdetails/print/{orderId}", new { controller = "Order", action = "PrintOrderDetails" }, new[] { "Nop.Web.Controllers" }); //order downloads routes.MapRoute("GetDownload", "download/getdownload/{orderItemId}/{agree}", new { controller = "Download", action = "GetDownload", agree = UrlParameter.Optional }, new { orderItemId = new GuidConstraint(false) }, new[] { "Nop.Web.Controllers" }); routes.MapRoute("GetLicense", "download/getlicense/{orderItemId}/", new { controller = "Download", action = "GetLicense" }, new { orderItemId = new GuidConstraint(false) }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("DownloadUserAgreement", "customer/useragreement/{orderItemId}", new { controller = "Customer", action = "UserAgreement" }, new { orderItemId = new GuidConstraint(false) }, new[] { "Nop.Web.Controllers" }); routes.MapRoute("GetOrderNoteFile", "download/ordernotefile/{ordernoteid}", new { controller = "Download", action = "GetOrderNoteFile" }, new { ordernoteid = @"\d+" }, new[] { "Nop.Web.Controllers" }); //contact vendor routes.MapLocalizedRoute("ContactVendor", "contactvendor/{vendorId}", new { controller = "Common", action = "ContactVendor" }, new[] { "Nop.Web.Controllers" }); //apply for vendor account routes.MapLocalizedRoute("ApplyVendorAccount", "vendor/apply", new { controller = "Vendor", action = "ApplyVendor" }, new[] { "Nop.Web.Controllers" }); //vendor info routes.MapLocalizedRoute("CustomerVendorInfo", "customer/vendorinfo", new { controller = "Vendor", action = "Info" }, new[] { "Nop.Web.Controllers" }); //poll vote AJAX link routes.MapLocalizedRoute("PollVote", "poll/vote", new { controller = "Poll", action = "Vote" }, new[] { "Nop.Web.Controllers" }); //comparing products routes.MapLocalizedRoute("RemoveProductFromCompareList", "compareproducts/remove/{productId}", new { controller = "Product", action = "RemoveProductFromCompareList" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("ClearCompareList", "clearcomparelist/", new { controller = "Product", action = "ClearCompareList" }, new[] { "Nop.Web.Controllers" }); //new RSS routes.MapLocalizedRoute("NewProductsRSS", "newproducts/rss", new { controller = "Product", action = "NewProductsRss" }, new[] { "Nop.Web.Controllers" }); //get state list by country ID (AJAX link) routes.MapRoute("GetStatesByCountryId", "country/getstatesbycountryid/", new { controller = "Country", action = "GetStatesByCountryId" }, new[] { "Nop.Web.Controllers" }); //EU Cookie law accept button handler (AJAX link) routes.MapRoute("EuCookieLawAccept", "eucookielawaccept", new { controller = "Common", action = "EuCookieLawAccept" }, new[] { "Nop.Web.Controllers" }); //authenticate topic AJAX link routes.MapLocalizedRoute("TopicAuthenticate", "topic/authenticate", new { controller = "Topic", action = "Authenticate" }, new[] { "Nop.Web.Controllers" }); //product attributes with "upload file" type routes.MapLocalizedRoute("UploadFileProductAttribute", "uploadfileproductattribute/{attributeId}", new { controller = "ShoppingCart", action = "UploadFileProductAttribute" }, new { attributeId = @"\d+" }, new[] { "Nop.Web.Controllers" }); //checkout attributes with "upload file" type routes.MapLocalizedRoute("UploadFileCheckoutAttribute", "uploadfilecheckoutattribute/{attributeId}", new { controller = "ShoppingCart", action = "UploadFileCheckoutAttribute" }, new { attributeId = @"\d+" }, new[] { "Nop.Web.Controllers" }); //return request with "upload file" tsupport routes.MapLocalizedRoute("UploadFileReturnRequest", "uploadfilereturnrequest", new { controller = "ReturnRequest", action = "UploadFileReturnRequest" }, new[] { "Nop.Web.Controllers" }); //forums routes.MapLocalizedRoute("ActiveDiscussions", "boards/activediscussions", new { controller = "Boards", action = "ActiveDiscussions" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("ActiveDiscussionsPaged", "boards/activediscussions/page/{page}", new { controller = "Boards", action = "ActiveDiscussions", page = UrlParameter.Optional }, new { page = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("ActiveDiscussionsRSS", "boards/activediscussionsrss", new { controller = "Boards", action = "ActiveDiscussionsRSS" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("PostEdit", "boards/postedit/{id}", new { controller = "Boards", action = "PostEdit" }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("PostDelete", "boards/postdelete/{id}", new { controller = "Boards", action = "PostDelete" }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("PostCreate", "boards/postcreate/{id}", new { controller = "Boards", action = "PostCreate" }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("PostCreateQuote", "boards/postcreate/{id}/{quote}", new { controller = "Boards", action = "PostCreate" }, new { id = @"\d+", quote = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("TopicEdit", "boards/topicedit/{id}", new { controller = "Boards", action = "TopicEdit" }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("TopicDelete", "boards/topicdelete/{id}", new { controller = "Boards", action = "TopicDelete" }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("TopicCreate", "boards/topiccreate/{id}", new { controller = "Boards", action = "TopicCreate" }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("TopicMove", "boards/topicmove/{id}", new { controller = "Boards", action = "TopicMove" }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("TopicWatch", "boards/topicwatch/{id}", new { controller = "Boards", action = "TopicWatch" }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("TopicSlug", "boards/topic/{id}/{slug}", new { controller = "Boards", action = "Topic", slug = UrlParameter.Optional }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("TopicSlugPaged", "boards/topic/{id}/{slug}/page/{page}", new { controller = "Boards", action = "Topic", slug = UrlParameter.Optional, page = UrlParameter.Optional }, new { id = @"\d+", page = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("ForumWatch", "boards/forumwatch/{id}", new { controller = "Boards", action = "ForumWatch" }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("ForumRSS", "boards/forumrss/{id}", new { controller = "Boards", action = "ForumRSS" }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("ForumSlug", "boards/forum/{id}/{slug}", new { controller = "Boards", action = "Forum", slug = UrlParameter.Optional }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("ForumSlugPaged", "boards/forum/{id}/{slug}/page/{page}", new { controller = "Boards", action = "Forum", slug = UrlParameter.Optional, page = UrlParameter.Optional }, new { id = @"\d+", page = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("ForumGroupSlug", "boards/forumgroup/{id}/{slug}", new { controller = "Boards", action = "ForumGroup", slug = UrlParameter.Optional }, new { id = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("Search", "boards/search", new { controller = "Boards", action = "Search" }, new[] { "Nop.Web.Controllers" }); //private messages routes.MapLocalizedRoute("PrivateMessages", "privatemessages/{tab}", new { controller = "PrivateMessages", action = "Index", tab = UrlParameter.Optional }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("PrivateMessagesPaged", "privatemessages/{tab}/page/{page}", new { controller = "PrivateMessages", action = "Index", tab = UrlParameter.Optional }, new { page = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("PrivateMessagesInbox", "inboxupdate", new { controller = "PrivateMessages", action = "InboxUpdate" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("PrivateMessagesSent", "sentupdate", new { controller = "PrivateMessages", action = "SentUpdate" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("SendPM", "sendpm/{toCustomerId}", new { controller = "PrivateMessages", action = "SendPM" }, new { toCustomerId = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("SendPMReply", "sendpm/{toCustomerId}/{replyToMessageId}", new { controller = "PrivateMessages", action = "SendPM" }, new { toCustomerId = @"\d+", replyToMessageId = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("ViewPM", "viewpm/{privateMessageId}", new { controller = "PrivateMessages", action = "ViewPM" }, new { privateMessageId = @"\d+" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("DeletePM", "deletepm/{privateMessageId}", new { controller = "PrivateMessages", action = "DeletePM" }, new { privateMessageId = @"\d+" }, new[] { "Nop.Web.Controllers" }); //activate newsletters routes.MapLocalizedRoute("NewsletterActivation", "newsletter/subscriptionactivation/{token}/{active}", new { controller = "Newsletter", action = "SubscriptionActivation" }, new { token = new GuidConstraint(false) }, new[] { "Nop.Web.Controllers" }); //robots.txt routes.MapRoute("robots.txt", "robots.txt", new { controller = "Common", action = "RobotsTextFile" }, new[] { "Nop.Web.Controllers" }); //sitemap (XML) routes.MapLocalizedRoute("sitemap.xml", "sitemap.xml", new { controller = "Common", action = "SitemapXml" }, new[] { "Nop.Web.Controllers" }); routes.MapLocalizedRoute("sitemap-indexed.xml", "sitemap-{Id}.xml", new { controller = "Common", action = "SitemapXml" }, new { Id = @"\d+" }, new[] { "Nop.Web.Controllers" }); //store closed routes.MapLocalizedRoute("StoreClosed", "storeclosed", new { controller = "Common", action = "StoreClosed" }, new[] { "Nop.Web.Controllers" }); //install routes.MapRoute("Installation", "install", new { controller = "Install", action = "Index" }, new[] { "Nop.Web.Controllers" }); //page not found routes.MapLocalizedRoute("PageNotFound", "page-not-found", new { controller = "Common", action = "PageNotFound" }, new[] { "Nop.Web.Controllers" }); } public int Priority { get { return 0; } } } }
Nop.Web.Infrastructure.RouteProvider
Nop.Web.Framework.Localization.LocalizedRouteExtensions类中对RouteCollection routes添加了MapLocalizedRoute的扩展方法
routes.MapLocalizedRoute("HomePage", "", new { controller = "Home", action = "Index" }, new[] { "Nop.Web.Controllers" });
Nop.Web.Framework.Localization.LocalizedRoute继承Route类进行扩展多语言Seo的支持
public static Route MapLocalizedRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { if (routes == null) { throw new ArgumentNullException("routes"); } if (url == null) { throw new ArgumentNullException("url"); } //LocalizedRoute 进行了多语言Seo优化
var route = new LocalizedRoute(url, new MvcRouteHandler()) { Defaults = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints), DataTokens = new RouteValueDictionary() }; if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; }
LocalizedRoute对路由进行了扩展,主要重写了GetRouteData,和GetVirtualPath这两个方法。
GetRouteData:解析Url,它的作用简单理解是通过url匹配到正确的路由。
开启多语言Seo后URL格式为: http://www.yourStore.com/en/ 或 http://www.yourStore.com/zh/ (SEO比较友好),
LocalizedRoute重写GetRouteData方法,会去掉en或zh后,匹配正确的路由位置。
例如输入http://localhost:15536/en,会正确的匹配到http://localhost:15536/
routes.MapLocalizedRoute("HomePage", "", new { controller = "Home", action = "Index" }, new[] { "Nop.Web.Controllers" });GetVirtualPath:生成Url,我们在Razor视图中常会看到<a href="@Url.Action("Index", "Home")">首页</a>这种标签,
GetVirtualPath负责将"@Url.Action("Index", "Home")"生成支持多语言Seo的url字符串http://www.yourStore.com/en/ ,自动会加上en或zh。
更好的理解GetRouteData和GetVirtualPath,可自行搜索下。
搜索引擎友好名称实现
除了对多语言Seo友好链接支持,nop还支持Seo友好链接的支持。
我们发现Nop.Web.Infrastructure.GenericUrlRouteProvider类主要用于Seo友好链接的路由注册,代码如下:
using System.Web.Routing; using Nop.Web.Framework.Localization; using Nop.Web.Framework.Mvc.Routes; using Nop.Web.Framework.Seo; namespace Nop.Web.Infrastructure { 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" }); //the last route. it's used when none of registered routes could be used for the current request //but in this case we cannot process non-registered routes (/controller/action) //routes.MapLocalizedRoute( // "PageNotFound-Wildchar", // "{*url}", // new { controller = "Common", action = "PageNotFound" }, // 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 overridden (if required) return -1000000; } } } }
Nop.Web.Infrastructure.GenericUrlRouteProvider
我们发现多了MapGenericPathRoute的扩展
//generic URLs routes.MapGenericPathRoute("GenericUrl", "{generic_se_name}", new {controller = "Common", action = "GenericUrl"}, new[] {"Nop.Web.Controllers"});
我们来看看MapGenericPathRoute方法来自Nop.Web.Framework.Seo.GenericPathRouteExtensions类中。
通过Nop.Web.Framework.Seo.GenericPathRoute完成Seo友好链接的路由扩展
public static Route MapGenericPathRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { if (routes == null) { throw new ArgumentNullException("routes"); } if (url == null) { throw new ArgumentNullException("url"); } //GenericPathRoute中实现Seo友好链接路由 var route = new GenericPathRoute(url, new MvcRouteHandler()) { Defaults = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints), DataTokens = new RouteValueDictionary() }; if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; }
GenericPathRoute对路由进行扩展,只重写了GetRouteData方法用于解析url,帮助路由到正确的执行位置。代码如下:
/// <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) { RouteData data = base.GetRouteData(httpContext); return null; if (data != null && DataSettingsHelper.DatabaseIsInstalled()) { var urlRecordService = EngineContext.Current.Resolve<IUrlRecordService>(); var slug = data.Values["generic_se_name"] as string; //performance optimization. //we load a cached verion here. it reduces number of SQL requests for each page load var urlRecord = urlRecordService.GetBySlugCached(slug); //comment the line above and uncomment the line below in order to disable this performance "workaround" //var urlRecord = urlRecordService.GetBySlug(slug); if (urlRecord == null) { //no URL record found //var webHelper = EngineContext.Current.Resolve<IWebHelper>(); //var response = httpContext.Response; //response.Status = "302 Found"; //response.RedirectLocation = webHelper.GetStoreLocation(false); //response.End(); //return null; data.Values["controller"] = "Common"; data.Values["action"] = "PageNotFound"; return data; } //ensure that URL record is active if (!urlRecord.IsActive) { //URL record is not active. let's find the latest one var activeSlug = urlRecordService.GetActiveSlug(urlRecord.EntityId, urlRecord.EntityName, urlRecord.LanguageId); if (string.IsNullOrWhiteSpace(activeSlug)) { //no active slug found //var webHelper = EngineContext.Current.Resolve<IWebHelper>(); //var response = httpContext.Response; //response.Status = "302 Found"; //response.RedirectLocation = webHelper.GetStoreLocation(false); //response.End(); //return null; data.Values["controller"] = "Common"; data.Values["action"] = "PageNotFound"; return data; } //the active one is found var webHelper = EngineContext.Current.Resolve<IWebHelper>(); var response = httpContext.Response; response.Status = "301 Moved Permanently"; response.RedirectLocation = string.Format("{0}{1}", webHelper.GetStoreLocation(), activeSlug); response.End(); return null; } //ensure that the slug is the same for the current language //otherwise, it can cause some issues when customers choose a new language but a slug stays the same var workContext = EngineContext.Current.Resolve<IWorkContext>(); var slugForCurrentLanguage = SeoExtensions.GetSeName(urlRecord.EntityId, urlRecord.EntityName, workContext.WorkingLanguage.Id); if (!String.IsNullOrEmpty(slugForCurrentLanguage) && !slugForCurrentLanguage.Equals(slug, StringComparison.InvariantCultureIgnoreCase)) { //we should make not null or "" validation above because some entities does not have SeName for standard (ID=0) language (e.g. news, blog posts) var webHelper = EngineContext.Current.Resolve<IWebHelper>(); var response = httpContext.Response; //response.Status = "302 Found"; response.Status = "302 Moved Temporarily"; response.RedirectLocation = string.Format("{0}{1}", webHelper.GetStoreLocation(), slugForCurrentLanguage); response.End(); return null; } //process URL switch (urlRecord.EntityName.ToLowerInvariant()) { case "product": { data.Values["controller"] = "Product"; data.Values["action"] = "ProductDetails"; data.Values["productid"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; case "category": { data.Values["controller"] = "Catalog"; data.Values["action"] = "Category"; data.Values["categoryid"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; case "manufacturer": { data.Values["controller"] = "Catalog"; data.Values["action"] = "Manufacturer"; data.Values["manufacturerid"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; case "vendor": { data.Values["controller"] = "Catalog"; data.Values["action"] = "Vendor"; data.Values["vendorid"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; case "newsitem": { data.Values["controller"] = "News"; data.Values["action"] = "NewsItem"; data.Values["newsItemId"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; case "blogpost": { data.Values["controller"] = "Blog"; data.Values["action"] = "BlogPost"; data.Values["blogPostId"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; case "topic": { data.Values["controller"] = "Topic"; data.Values["action"] = "TopicDetails"; data.Values["topicId"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; default: { //no record found //generate an event this way developers could insert their own types EngineContext.Current.Resolve<IEventPublisher>() .Publish(new CustomUrlRecordEntityNameRequested(data, urlRecord)); } break; } } return data; }
GetRouteData
路由匹配到支持Seo友好链接会重定向链接新的路由位置,nop 3.9目前支持下图中的路由
提示:数据库UrlRecord表对Seo友情链接提供支持。
总结
1.继承IRouteProvider对路由进行扩展。(注意路由注册顺序,ASP.NET MVC 只匹配第一次成功的路由信息)
2.多语言Seo友好链接通过LocalizedRoute类进行路由扩展(MapLocalizedRoute扩展中调用)
3.页面Seo友好链接通过GenericPathRoute类进行路由扩展(MapGenericPathRoute扩展中调用)
文中有错误的理解和不正确的观点,请留言,一起交流共同进步。
最新文章
- OpenCV图像细化的一个例子
- LoadRunner 12试用
- 34.Android之资源文件res里drawable学习
- kail ip配置
- JavaScript基于对象(面向对象)<;一>;类和对象
- 十六、Android 滑动效果汇总
- Channel Allocation(四色定理 dfs)
- Tomcat 配置WEB虚拟映射 及 配置虚拟主机
- .htaccess重写URL讲解
- 红包项目总结---MVC版
- 0.搭建myeclipse开发环境
- Linux系列教程(二十)——Linux的shell概述以及如何执行脚本
- java 重定向和转发的区别
- Python 学习 第十一篇:numpy
- # NOI.AC省选赛 第五场T1 子集,与&;最大值
- Spring Boot Actuator认识
- Macbook pro开启允许任何源
- python字典dict的增、删、改、查操作
- OneAPM NI 基于旁路镜像数据的真实用户体验监控
- 向windows添加环境变量