nopCommerce 3.9 大波浪系列 之 路由扩展 [多语言Seo的实现]

一.nop中的路由注册

在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)方法进行路由注册。

下图为主要接口之间的调用关系

nopCommerce 3.9 大波浪系列 之 路由扩展 [多语言Seo的实现]

通过上述分析nop路由注册主要是通过IRouteProvider接口实现的,现在我们看看项目中实现该接口的类都有哪些。

下图红色是在Nop.Web项目中,其他都是在nop插件中。

nopCommerce 3.9 大波浪系列 之 路由扩展 [多语言Seo的实现]

接下来我们看下路由的注册顺序如下图,我们看到最先匹配的Nop.Web.Infrastructure.RouteProvider中的路由注册。

nopCommerce 3.9 大波浪系列 之 路由扩展 [多语言Seo的实现]

二.启用支持多语言的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友好链接的支持。

nopCommerce 3.9 大波浪系列 之 路由扩展 [多语言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友情链接提供支持。

nopCommerce 3.9 大波浪系列 之 路由扩展 [多语言Seo的实现]

四.总结

1.继承IRouteProvider对路由进行扩展。(注意路由注册顺序,ASP.NET MVC 只匹配第一次成功的路由信息)

2.多语言Seo友好链接通过LocalizedRoute类进行路由扩展(MapLocalizedRoute扩展中调用)

3.页面Seo友好链接通过GenericPathRoute类进行路由扩展(MapGenericPathRoute扩展中调用)

文中有错误的理解和不正确的观点,请留言,一起交流共同进步。

本文地址:http://www.cnblogs.com/yaoshangjin/p/7228177.html

本文为大波浪原创、转载请注明出处。

上一篇:[译]SQL Server 之 索引基础


下一篇:Leetcode Sum Root to Leaf Numbers