在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扩展中调用)

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

最新文章

  1. OpenCV图像细化的一个例子
  2. LoadRunner 12试用
  3. 34.Android之资源文件res里drawable学习
  4. kail ip配置
  5. JavaScript基于对象(面向对象)&lt;一&gt;类和对象
  6. 十六、Android 滑动效果汇总
  7. Channel Allocation(四色定理 dfs)
  8. Tomcat 配置WEB虚拟映射 及 配置虚拟主机
  9. .htaccess重写URL讲解
  10. 红包项目总结---MVC版
  11. 0.搭建myeclipse开发环境
  12. Linux系列教程(二十)——Linux的shell概述以及如何执行脚本
  13. java 重定向和转发的区别
  14. Python 学习 第十一篇:numpy
  15. # NOI.AC省选赛 第五场T1 子集,与&amp;最大值
  16. Spring Boot Actuator认识
  17. Macbook pro开启允许任何源
  18. python字典dict的增、删、改、查操作
  19. OneAPM NI 基于旁路镜像数据的真实用户体验监控
  20. 向windows添加环境变量

热门文章

  1. 使用React改版网站后的一些感想
  2. iOS CAShapeLayer、CADisplayLink 实现波浪动画效果
  3. 【基础】C#异常处理的总结
  4. EJB基础知识
  5. angular多页面切换传递参数
  6. [css 实践篇] CSS box-orient
  7. .net 4.0 中的特性总结(五):并行编程
  8. Linux 中最常用的目录及文件管理命令
  9. 【Android Developers Training】 32. 向其它应用发送简单数据
  10. Example013操作样式