一.nop中的路由注册
在Global.asax,Application_Start()方法中会进行路由注册,代码如下。
1 public static void RegisterRoutes(RouteCollection routes)
2 {
3 routes.IgnoreRoute("favicon.ico");
4 routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
5
6 //register custom routes (plugins, etc)
7 var routePublisher = EngineContext.Current.Resolve<IRoutePublisher>();
8 routePublisher.RegisterRoutes(routes);
9
10 routes.MapRoute(
11 "Default", // Route name
12 "{controller}/{action}/{id}", // URL with parameters
13 new { controller = "Home", action = "Index", id = UrlParameter.Optional },
14 new[] { "Nop.Web.Controllers" }
15 );
16 }
我们会发现调用了IRutePublisher接口,该接口由Nop.Web.Framework.Mvc.Routes.RoutePublisher类实现。
并通过RegisterRoutes(RouteCollection routes)方法进行路由注册。代码如下:
1 /// <summary>
2 /// Register routes
3 /// </summary>
4 /// <param name="routes">Routes</param>
5 public virtual void RegisterRoutes(RouteCollection routes)
6 {
7 //ITypeFinder接口获取所有IRouteProvider接口实现类的Type对象
8 var routeProviderTypes = typeFinder.FindClassesOfType<IRouteProvider>();
9 var routeProviders = new List<IRouteProvider>();
10 foreach (var providerType in routeProviderTypes)
11 {
12 //Ignore not installed plugins
13 var plugin = FindPlugin(providerType);//PluginManager.ReferencedPlugins中查找该Type对象
14 if (plugin != null && !plugin.Installed)//插件不为空未安装则跳出本次循环
15 continue;
16
17 var provider = Activator.CreateInstance(providerType) as IRouteProvider;//实例化IRouteProvider接口对象
18 routeProviders.Add(provider);
19 }
20 routeProviders = routeProviders.OrderByDescending(rp => rp.Priority).ToList();//排序
21 routeProviders.ForEach(rp => rp.RegisterRoutes(routes));//遍历并调用RegisterRoutes(routes)进行路由注册
22 }
该方法做了如下工作:
第一步:通过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中的路由注册代码
1 using System.Web.Mvc;
2 using System.Web.Routing;
3 using Nop.Web.Framework.Localization;
4 using Nop.Web.Framework.Mvc.Routes;
5
6 namespace Nop.Web.Infrastructure
7 {
8 public partial class RouteProvider : IRouteProvider
9 {
10 public void RegisterRoutes(RouteCollection routes)
11 {
12 //We reordered our routes so the most used ones are on top. It can improve performance.
13 //本地化路由
14 //home page
15 routes.MapLocalizedRoute("HomePage",
16 "",
17 new { controller = "Home", action = "Index" },
18 new[] { "Nop.Web.Controllers" });
19 //widgets
20 //we have this route for performance optimization because named routes are MUCH faster than usual Html.Action(...)
21 //and this route is highly used
22 routes.MapRoute("WidgetsByZone",
23 "widgetsbyzone/",
24 new { controller = "Widget", action = "WidgetsByZone" },
25 new[] { "Nop.Web.Controllers" });
26
27 //login
28 routes.MapLocalizedRoute("Login",
29 "login/",
30 new { controller = "Customer", action = "Login" },
31 new[] { "Nop.Web.Controllers" });
32 //register
33 routes.MapLocalizedRoute("Register",
34 "register/",
35 new { controller = "Customer", action = "Register" },
36 new[] { "Nop.Web.Controllers" });
37 //logout
38 routes.MapLocalizedRoute("Logout",
39 "logout/",
40 new { controller = "Customer", action = "Logout" },
41 new[] { "Nop.Web.Controllers" });
42
43 //shopping cart
44 routes.MapLocalizedRoute("ShoppingCart",
45 "cart/",
46 new { controller = "ShoppingCart", action = "Cart" },
47 new[] { "Nop.Web.Controllers" });
48 //estimate shipping
49 routes.MapLocalizedRoute("EstimateShipping",
50 "cart/estimateshipping",
51 new {controller = "ShoppingCart", action = "GetEstimateShipping"},
52 new[] {"Nop.Web.Controllers"});
53 //wishlist
54 routes.MapLocalizedRoute("Wishlist",
55 "wishlist/{customerGuid}",
56 new { controller = "ShoppingCart", action = "Wishlist", customerGuid = UrlParameter.Optional },
57 new[] { "Nop.Web.Controllers" });
58
59 //customer account links
60 routes.MapLocalizedRoute("CustomerInfo",
61 "customer/info",
62 new { controller = "Customer", action = "Info" },
63 new[] { "Nop.Web.Controllers" });
64 routes.MapLocalizedRoute("CustomerAddresses",
65 "customer/addresses",
66 new { controller = "Customer", action = "Addresses" },
67 new[] { "Nop.Web.Controllers" });
68 routes.MapLocalizedRoute("CustomerOrders",
69 "order/history",
70 new { controller = "Order", action = "CustomerOrders" },
71 new[] { "Nop.Web.Controllers" });
72
73 //contact us
74 routes.MapLocalizedRoute("ContactUs",
75 "contactus",
76 new { controller = "Common", action = "ContactUs" },
77 new[] { "Nop.Web.Controllers" });
78 //sitemap
79 routes.MapLocalizedRoute("Sitemap",
80 "sitemap",
81 new { controller = "Common", action = "Sitemap" },
82 new[] { "Nop.Web.Controllers" });
83
84 //product search
85 routes.MapLocalizedRoute("ProductSearch",
86 "search/",
87 new { controller = "Catalog", action = "Search" },
88 new[] { "Nop.Web.Controllers" });
89 routes.MapLocalizedRoute("ProductSearchAutoComplete",
90 "catalog/searchtermautocomplete",
91 new { controller = "Catalog", action = "SearchTermAutoComplete" },
92 new[] { "Nop.Web.Controllers" });
93
94 //change currency (AJAX link)
95 routes.MapLocalizedRoute("ChangeCurrency",
96 "changecurrency/{customercurrency}",
97 new { controller = "Common", action = "SetCurrency" },
98 new { customercurrency = @"d+" },
99 new[] { "Nop.Web.Controllers" });
100 //change language (AJAX link)
101 routes.MapLocalizedRoute("ChangeLanguage",
102 "changelanguage/{langid}",
103 new { controller = "Common", action = "SetLanguage" },
104 new { langid = @"d+" },
105 new[] { "Nop.Web.Controllers" });
106 //change tax (AJAX link)
107 routes.MapLocalizedRoute("ChangeTaxType",
108 "changetaxtype/{customertaxtype}",
109 new { controller = "Common", action = "SetTaxType" },
110 new { customertaxtype = @"d+" },
111 new[] { "Nop.Web.Controllers" });
112
113 //recently viewed products
114 routes.MapLocalizedRoute("RecentlyViewedProducts",
115 "recentlyviewedproducts/",
116 new { controller = "Product", action = "RecentlyViewedProducts" },
117 new[] { "Nop.Web.Controllers" });
118 //new products
119 routes.MapLocalizedRoute("NewProducts",
120 "newproducts/",
121 new { controller = "Product", action = "NewProducts" },
122 new[] { "Nop.Web.Controllers" });
123 //blog
124 routes.MapLocalizedRoute("Blog",
125 "blog",
126 new { controller = "Blog", action = "List" },
127 new[] { "Nop.Web.Controllers" });
128 //news
129 routes.MapLocalizedRoute("NewsArchive",
130 "news",
131 new { controller = "News", action = "List" },
132 new[] { "Nop.Web.Controllers" });
133
134 //forum
135 routes.MapLocalizedRoute("Boards",
136 "boards",
137 new { controller = "Boards", action = "Index" },
138 new[] { "Nop.Web.Controllers" });
139
140 //compare products
141 routes.MapLocalizedRoute("CompareProducts",
142 "compareproducts/",
143 new { controller = "Product", action = "CompareProducts" },
144 new[] { "Nop.Web.Controllers" });
145
146 //product tags
147 routes.MapLocalizedRoute("ProductTagsAll",
148 "producttag/all/",
149 new { controller = "Catalog", action = "ProductTagsAll" },
150 new[] { "Nop.Web.Controllers" });
151
152 //manufacturers
153 routes.MapLocalizedRoute("ManufacturerList",
154 "manufacturer/all/",
155 new { controller = "Catalog", action = "ManufacturerAll" },
156 new[] { "Nop.Web.Controllers" });
157 //vendors
158 routes.MapLocalizedRoute("VendorList",
159 "vendor/all/",
160 new { controller = "Catalog", action = "VendorAll" },
161 new[] { "Nop.Web.Controllers" });
162
163
164 //add product to cart (without any attributes and options). used on catalog pages.
165 routes.MapLocalizedRoute("AddProductToCart-Catalog",
166 "addproducttocart/catalog/{productId}/{shoppingCartTypeId}/{quantity}",
167 new { controller = "ShoppingCart", action = "AddProductToCart_Catalog" },
168 new { productId = @"d+", shoppingCartTypeId = @"d+", quantity = @"d+" },
169 new[] { "Nop.Web.Controllers" });
170 //add product to cart (with attributes and options). used on the product details pages.
171 routes.MapLocalizedRoute("AddProductToCart-Details",
172 "addproducttocart/details/{productId}/{shoppingCartTypeId}",
173 new { controller = "ShoppingCart", action = "AddProductToCart_Details" },
174 new { productId = @"d+", shoppingCartTypeId = @"d+" },
175 new[] { "Nop.Web.Controllers" });
176
177 //product tags
178 routes.MapLocalizedRoute("ProductsByTag",
179 "producttag/{productTagId}/{SeName}",
180 new { controller = "Catalog", action = "ProductsByTag", SeName = UrlParameter.Optional },
181 new { productTagId = @"d+" },
182 new[] { "Nop.Web.Controllers" });
183 //comparing products
184 routes.MapLocalizedRoute("AddProductToCompare",
185 "compareproducts/add/{productId}",
186 new { controller = "Product", action = "AddProductToCompareList" },
187 new { productId = @"d+" },
188 new[] { "Nop.Web.Controllers" });
189 //product email a friend
190 routes.MapLocalizedRoute("ProductEmailAFriend",
191 "productemailafriend/{productId}",
192 new { controller = "Product", action = "ProductEmailAFriend" },
193 new { productId = @"d+" },
194 new[] { "Nop.Web.Controllers" });
195 //reviews
196 routes.MapLocalizedRoute("ProductReviews",
197 "productreviews/{productId}",
198 new { controller = "Product", action = "ProductReviews" },
199 new[] { "Nop.Web.Controllers" });
200 routes.MapLocalizedRoute("CustomerProductReviews",
201 "customer/productreviews",
202 new { controller = "Product", action = "CustomerProductReviews" },
203 new[] { "Nop.Web.Controllers" });
204 routes.MapLocalizedRoute("CustomerProductReviewsPaged",
205 "customer/productreviews/page/{page}",
206 new { controller = "Product", action = "CustomerProductReviews" },
207 new { page = @"d+" },
208 new[] { "Nop.Web.Controllers" });
209 //back in stock notifications
210 routes.MapLocalizedRoute("BackInStockSubscribePopup",
211 "backinstocksubscribe/{productId}",
212 new { controller = "BackInStockSubscription", action = "SubscribePopup" },
213 new { productId = @"d+" },
214 new[] { "Nop.Web.Controllers" });
215 routes.MapLocalizedRoute("BackInStockSubscribeSend",
216 "backinstocksubscribesend/{productId}",
217 new { controller = "BackInStockSubscription", action = "SubscribePopupPOST" },
218 new { productId = @"d+" },
219 new[] { "Nop.Web.Controllers" });
220 //downloads
221 routes.MapRoute("GetSampleDownload",
222 "download/sample/{productid}",
223 new { controller = "Download", action = "Sample" },
224 new { productid = @"d+" },
225 new[] { "Nop.Web.Controllers" });
226
227
228
229 //checkout pages
230 routes.MapLocalizedRoute("Checkout",
231 "checkout/",
232 new { controller = "Checkout", action = "Index" },
233 new[] { "Nop.Web.Controllers" });
234 routes.MapLocalizedRoute("CheckoutOnePage",
235 "onepagecheckout/",
236 new { controller = "Checkout", action = "OnePageCheckout" },
237 new[] { "Nop.Web.Controllers" });
238 routes.MapLocalizedRoute("CheckoutShippingAddress",
239 "checkout/shippingaddress",
240 new { controller = "Checkout", action = "ShippingAddress" },
241 new[] { "Nop.Web.Controllers" });
242 routes.MapLocalizedRoute("CheckoutSelectShippingAddress",
243 "checkout/selectshippingaddress",
244 new { controller = "Checkout", action = "SelectShippingAddress" },
245 new[] { "Nop.Web.Controllers" });
246 routes.MapLocalizedRoute("CheckoutBillingAddress",
247 "checkout/billingaddress",
248 new { controller = "Checkout", action = "BillingAddress" },
249 new[] { "Nop.Web.Controllers" });
250 routes.MapLocalizedRoute("CheckoutSelectBillingAddress",
251 "checkout/selectbillingaddress",
252 new { controller = "Checkout", action = "SelectBillingAddress" },
253 new[] { "Nop.Web.Controllers" });
254 routes.MapLocalizedRoute("CheckoutShippingMethod",
255 "checkout/shippingmethod",
256 new { controller = "Checkout", action = "ShippingMethod" },
257 new[] { "Nop.Web.Controllers" });
258 routes.MapLocalizedRoute("CheckoutPaymentMethod",
259 "checkout/paymentmethod",
260 new { controller = "Checkout", action = "PaymentMethod" },
261 new[] { "Nop.Web.Controllers" });
262 routes.MapLocalizedRoute("CheckoutPaymentInfo",
263 "checkout/paymentinfo",
264 new { controller = "Checkout", action = "PaymentInfo" },
265 new[] { "Nop.Web.Controllers" });
266 routes.MapLocalizedRoute("CheckoutConfirm",
267 "checkout/confirm",
268 new { controller = "Checkout", action = "Confirm" },
269 new[] { "Nop.Web.Controllers" });
270 routes.MapLocalizedRoute("CheckoutCompleted",
271 "checkout/completed/{orderId}",
272 new { controller = "Checkout", action = "Completed", orderId = UrlParameter.Optional },
273 new { orderId = @"d+" },
274 new[] { "Nop.Web.Controllers" });
275
276 //subscribe newsletters
277 routes.MapLocalizedRoute("SubscribeNewsletter",
278 "subscribenewsletter",
279 new { controller = "Newsletter", action = "SubscribeNewsletter" },
280 new[] { "Nop.Web.Controllers" });
281
282 //email wishlist
283 routes.MapLocalizedRoute("EmailWishlist",
284 "emailwishlist",
285 new { controller = "ShoppingCart", action = "EmailWishlist" },
286 new[] { "Nop.Web.Controllers" });
287
288 //login page for checkout as guest
289 routes.MapLocalizedRoute("LoginCheckoutAsGuest",
290 "login/checkoutasguest",
291 new { controller = "Customer", action = "Login", checkoutAsGuest = true },
292 new[] { "Nop.Web.Controllers" });
293 //register result page
294 routes.MapLocalizedRoute("RegisterResult",
295 "registerresult/{resultId}",
296 new { controller = "Customer", action = "RegisterResult" },
297 new { resultId = @"d+" },
298 new[] { "Nop.Web.Controllers" });
299 //check username availability
300 routes.MapLocalizedRoute("CheckUsernameAvailability",
301 "customer/checkusernameavailability",
302 new { controller = "Customer", action = "CheckUsernameAvailability" },
303 new[] { "Nop.Web.Controllers" });
304
305 //passwordrecovery
306 routes.MapLocalizedRoute("PasswordRecovery",
307 "passwordrecovery",
308 new { controller = "Customer", action = "PasswordRecovery" },
309 new[] { "Nop.Web.Controllers" });
310 //password recovery confirmation
311 routes.MapLocalizedRoute("PasswordRecoveryConfirm",
312 "passwordrecovery/confirm",
313 new { controller = "Customer", action = "PasswordRecoveryConfirm" },
314 new[] { "Nop.Web.Controllers" });
315
316 //topics
317 routes.MapLocalizedRoute("TopicPopup",
318 "t-popup/{SystemName}",
319 new { controller = "Topic", action = "TopicDetailsPopup" },
320 new[] { "Nop.Web.Controllers" });
321
322 //blog
323 routes.MapLocalizedRoute("BlogByTag",
324 "blog/tag/{tag}",
325 new { controller = "Blog", action = "BlogByTag" },
326 new[] { "Nop.Web.Controllers" });
327 routes.MapLocalizedRoute("BlogByMonth",
328 "blog/month/{month}",
329 new { controller = "Blog", action = "BlogByMonth" },
330 new[] { "Nop.Web.Controllers" });
331 //blog RSS
332 routes.MapLocalizedRoute("BlogRSS",
333 "blog/rss/{languageId}",
334 new { controller = "Blog", action = "ListRss" },
335 new { languageId = @"d+" },
336 new[] { "Nop.Web.Controllers" });
337
338 //news RSS
339 routes.MapLocalizedRoute("NewsRSS",
340 "news/rss/{languageId}",
341 new { controller = "News", action = "ListRss" },
342 new { languageId = @"d+" },
343 new[] { "Nop.Web.Controllers" });
344
345 //set review helpfulness (AJAX link)
346 routes.MapRoute("SetProductReviewHelpfulness",
347 "setproductreviewhelpfulness",
348 new { controller = "Product", action = "SetProductReviewHelpfulness" },
349 new[] { "Nop.Web.Controllers" });
350
351 //customer account links
352 routes.MapLocalizedRoute("CustomerReturnRequests",
353 "returnrequest/history",
354 new { controller = "ReturnRequest", action = "CustomerReturnRequests" },
355 new[] { "Nop.Web.Controllers" });
356 routes.MapLocalizedRoute("CustomerDownloadableProducts",
357 "customer/downloadableproducts",
358 new { controller = "Customer", action = "DownloadableProducts" },
359 new[] { "Nop.Web.Controllers" });
360 routes.MapLocalizedRoute("CustomerBackInStockSubscriptions",
361 "backinstocksubscriptions/manage",
362 new { controller = "BackInStockSubscription", action = "CustomerSubscriptions" },
363 new[] { "Nop.Web.Controllers" });
364 routes.MapLocalizedRoute("CustomerBackInStockSubscriptionsPaged",
365 "backinstocksubscriptions/manage/{page}",
366 new { controller = "BackInStockSubscription", action = "CustomerSubscriptions", page = UrlParameter.Optional },
367 new { page = @"d+" },
368 new[] { "Nop.Web.Controllers" });
369 routes.MapLocalizedRoute("CustomerRewardPoints",
370 "rewardpoints/history",
371 new { controller = "Order", action = "CustomerRewardPoints" },
372 new[] { "Nop.Web.Controllers" });
373 routes.MapLocalizedRoute("CustomerRewardPointsPaged",
374 "rewardpoints/history/page/{page}",
375 new { controller = "Order", action = "CustomerRewardPoints" },
376 new { page = @"d+" },
377 new[] { "Nop.Web.Controllers" });
378 routes.MapLocalizedRoute("CustomerChangePassword",
379 "customer/changepassword",
380 new { controller = "Customer", action = "ChangePassword" },
381 new[] { "Nop.Web.Controllers" });
382 routes.MapLocalizedRoute("CustomerAvatar",
383 "customer/avatar",
384 new { controller = "Customer", action = "Avatar" },
385 new[] { "Nop.Web.Controllers" });
386 routes.MapLocalizedRoute("AccountActivation",
387 "customer/activation",
388 new { controller = "Customer", action = "AccountActivation" },
389 new[] { "Nop.Web.Controllers" });
390 routes.MapLocalizedRoute("EmailRevalidation",
391 "customer/revalidateemail",
392 new { controller = "Customer", action = "EmailRevalidation" },
393 new[] { "Nop.Web.Controllers" });
394 routes.MapLocalizedRoute("CustomerForumSubscriptions",
395 "boards/forumsubscriptions",
396 new { controller = "Boards", action = "CustomerForumSubscriptions" },
397 new[] { "Nop.Web.Controllers" });
398 routes.MapLocalizedRoute("CustomerForumSubscriptionsPaged",
399 "boards/forumsubscriptions/{page}",
400 new { controller = "Boards", action = "CustomerForumSubscriptions", page = UrlParameter.Optional },
401 new { page = @"d+" },
402 new[] { "Nop.Web.Controllers" });
403 routes.MapLocalizedRoute("CustomerAddressEdit",
404 "customer/addressedit/{addressId}",
405 new { controller = "Customer", action = "AddressEdit" },
406 new { addressId = @"d+" },
407 new[] { "Nop.Web.Controllers" });
408 routes.MapLocalizedRoute("CustomerAddressAdd",
409 "customer/addressadd",
410 new { controller = "Customer", action = "AddressAdd" },
411 new[] { "Nop.Web.Controllers" });
412 //customer profile page
413 routes.MapLocalizedRoute("CustomerProfile",
414 "profile/{id}",
415 new { controller = "Profile", action = "Index" },
416 new { id = @"d+" },
417 new[] { "Nop.Web.Controllers" });
418 routes.MapLocalizedRoute("CustomerProfilePaged",
419 "profile/{id}/page/{page}",
420 new { controller = "Profile", action = "Index" },
421 new { id = @"d+", page = @"d+" },
422 new[] { "Nop.Web.Controllers" });
423
424 //orders
425 routes.MapLocalizedRoute("OrderDetails",
426 "orderdetails/{orderId}",
427 new { controller = "Order", action = "Details" },
428 new { orderId = @"d+" },
429 new[] { "Nop.Web.Controllers" });
430 routes.MapLocalizedRoute("ShipmentDetails",
431 "orderdetails/shipment/{shipmentId}",
432 new { controller = "Order", action = "ShipmentDetails" },
433 new[] { "Nop.Web.Controllers" });
434 routes.MapLocalizedRoute("ReturnRequest",
435 "returnrequest/{orderId}",
436 new { controller = "ReturnRequest", action = "ReturnRequest" },
437 new { orderId = @"d+" },
438 new[] { "Nop.Web.Controllers" });
439 routes.MapLocalizedRoute("ReOrder",
440 "reorder/{orderId}",
441 new { controller = "Order", action = "ReOrder" },
442 new { orderId = @"d+" },
443 new[] { "Nop.Web.Controllers" });
444 routes.MapLocalizedRoute("GetOrderPdfInvoice",
445 "orderdetails/pdf/{orderId}",
446 new { controller = "Order", action = "GetPdfInvoice" },
447 new[] { "Nop.Web.Controllers" });
448 routes.MapLocalizedRoute("PrintOrderDetails",
449 "orderdetails/print/{orderId}",
450 new { controller = "Order", action = "PrintOrderDetails" },
451 new[] { "Nop.Web.Controllers" });
452 //order downloads
453 routes.MapRoute("GetDownload",
454 "download/getdownload/{orderItemId}/{agree}",
455 new { controller = "Download", action = "GetDownload", agree = UrlParameter.Optional },
456 new { orderItemId = new GuidConstraint(false) },
457 new[] { "Nop.Web.Controllers" });
458 routes.MapRoute("GetLicense",
459 "download/getlicense/{orderItemId}/",
460 new { controller = "Download", action = "GetLicense" },
461 new { orderItemId = new GuidConstraint(false) },
462 new[] { "Nop.Web.Controllers" });
463 routes.MapLocalizedRoute("DownloadUserAgreement",
464 "customer/useragreement/{orderItemId}",
465 new { controller = "Customer", action = "UserAgreement" },
466 new { orderItemId = new GuidConstraint(false) },
467 new[] { "Nop.Web.Controllers" });
468 routes.MapRoute("GetOrderNoteFile",
469 "download/ordernotefile/{ordernoteid}",
470 new { controller = "Download", action = "GetOrderNoteFile" },
471 new { ordernoteid = @"d+" },
472 new[] { "Nop.Web.Controllers" });
473
474 //contact vendor
475 routes.MapLocalizedRoute("ContactVendor",
476 "contactvendor/{vendorId}",
477 new { controller = "Common", action = "ContactVendor" },
478 new[] { "Nop.Web.Controllers" });
479 //apply for vendor account
480 routes.MapLocalizedRoute("ApplyVendorAccount",
481 "vendor/apply",
482 new { controller = "Vendor", action = "ApplyVendor" },
483 new[] { "Nop.Web.Controllers" });
484 //vendor info
485 routes.MapLocalizedRoute("CustomerVendorInfo",
486 "customer/vendorinfo",
487 new { controller = "Vendor", action = "Info" },
488 new[] { "Nop.Web.Controllers" });
489
490 //poll vote AJAX link
491 routes.MapLocalizedRoute("PollVote",
492 "poll/vote",
493 new { controller = "Poll", action = "Vote" },
494 new[] { "Nop.Web.Controllers" });
495
496 //comparing products
497 routes.MapLocalizedRoute("RemoveProductFromCompareList",
498 "compareproducts/remove/{productId}",
499 new { controller = "Product", action = "RemoveProductFromCompareList" },
500 new[] { "Nop.Web.Controllers" });
501 routes.MapLocalizedRoute("ClearCompareList",
502 "clearcomparelist/",
503 new { controller = "Product", action = "ClearCompareList" },
504 new[] { "Nop.Web.Controllers" });
505
506 //new RSS
507 routes.MapLocalizedRoute("NewProductsRSS",
508 "newproducts/rss",
509 new { controller = "Product", action = "NewProductsRss" },
510 new[] { "Nop.Web.Controllers" });
511
512 //get state list by country ID (AJAX link)
513 routes.MapRoute("GetStatesByCountryId",
514 "country/getstatesbycountryid/",
515 new { controller = "Country", action = "GetStatesByCountryId" },
516 new[] { "Nop.Web.Controllers" });
517
518 //EU Cookie law accept button handler (AJAX link)
519 routes.MapRoute("EuCookieLawAccept",
520 "eucookielawaccept",
521 new { controller = "Common", action = "EuCookieLawAccept" },
522 new[] { "Nop.Web.Controllers" });
523
524 //authenticate topic AJAX link
525 routes.MapLocalizedRoute("TopicAuthenticate",
526 "topic/authenticate",
527 new { controller = "Topic", action = "Authenticate" },
528 new[] { "Nop.Web.Controllers" });
529
530 //product attributes with "upload file" type
531 routes.MapLocalizedRoute("UploadFileProductAttribute",
532 "uploadfileproductattribute/{attributeId}",
533 new { controller = "ShoppingCart", action = "UploadFileProductAttribute" },
534 new { attributeId = @"d+" },
535 new[] { "Nop.Web.Controllers" });
536 //checkout attributes with "upload file" type
537 routes.MapLocalizedRoute("UploadFileCheckoutAttribute",
538 "uploadfilecheckoutattribute/{attributeId}",
539 new { controller = "ShoppingCart", action = "UploadFileCheckoutAttribute" },
540 new { attributeId = @"d+" },
541 new[] { "Nop.Web.Controllers" });
542 //return request with "upload file" tsupport
543 routes.MapLocalizedRoute("UploadFileReturnRequest",
544 "uploadfilereturnrequest",
545 new { controller = "ReturnRequest", action = "UploadFileReturnRequest" },
546 new[] { "Nop.Web.Controllers" });
547
548 //forums
549 routes.MapLocalizedRoute("ActiveDiscussions",
550 "boards/activediscussions",
551 new { controller = "Boards", action = "ActiveDiscussions" },
552 new[] { "Nop.Web.Controllers" });
553 routes.MapLocalizedRoute("ActiveDiscussionsPaged",
554 "boards/activediscussions/page/{page}",
555 new { controller = "Boards", action = "ActiveDiscussions", page = UrlParameter.Optional },
556 new { page = @"d+" },
557 new[] { "Nop.Web.Controllers" });
558 routes.MapLocalizedRoute("ActiveDiscussionsRSS",
559 "boards/activediscussionsrss",
560 new { controller = "Boards", action = "ActiveDiscussionsRSS" },
561 new[] { "Nop.Web.Controllers" });
562 routes.MapLocalizedRoute("PostEdit",
563 "boards/postedit/{id}",
564 new { controller = "Boards", action = "PostEdit" },
565 new { id = @"d+" },
566 new[] { "Nop.Web.Controllers" });
567 routes.MapLocalizedRoute("PostDelete",
568 "boards/postdelete/{id}",
569 new { controller = "Boards", action = "PostDelete" },
570 new { id = @"d+" },
571 new[] { "Nop.Web.Controllers" });
572 routes.MapLocalizedRoute("PostCreate",
573 "boards/postcreate/{id}",
574 new { controller = "Boards", action = "PostCreate" },
575 new { id = @"d+" },
576 new[] { "Nop.Web.Controllers" });
577 routes.MapLocalizedRoute("PostCreateQuote",
578 "boards/postcreate/{id}/{quote}",
579 new { controller = "Boards", action = "PostCreate" },
580 new { id = @"d+", quote = @"d+" },
581 new[] { "Nop.Web.Controllers" });
582 routes.MapLocalizedRoute("TopicEdit",
583 "boards/topicedit/{id}",
584 new { controller = "Boards", action = "TopicEdit" },
585 new { id = @"d+" },
586 new[] { "Nop.Web.Controllers" });
587 routes.MapLocalizedRoute("TopicDelete",
588 "boards/topicdelete/{id}",
589 new { controller = "Boards", action = "TopicDelete" },
590 new { id = @"d+" },
591 new[] { "Nop.Web.Controllers" });
592 routes.MapLocalizedRoute("TopicCreate",
593 "boards/topiccreate/{id}",
594 new { controller = "Boards", action = "TopicCreate" },
595 new { id = @"d+" },
596 new[] { "Nop.Web.Controllers" });
597 routes.MapLocalizedRoute("TopicMove",
598 "boards/topicmove/{id}",
599 new { controller = "Boards", action = "TopicMove" },
600 new { id = @"d+" },
601 new[] { "Nop.Web.Controllers" });
602 routes.MapLocalizedRoute("TopicWatch",
603 "boards/topicwatch/{id}",
604 new { controller = "Boards", action = "TopicWatch" },
605 new { id = @"d+" },
606 new[] { "Nop.Web.Controllers" });
607 routes.MapLocalizedRoute("TopicSlug",
608 "boards/topic/{id}/{slug}",
609 new { controller = "Boards", action = "Topic", slug = UrlParameter.Optional },
610 new { id = @"d+" },
611 new[] { "Nop.Web.Controllers" });
612 routes.MapLocalizedRoute("TopicSlugPaged",
613 "boards/topic/{id}/{slug}/page/{page}",
614 new { controller = "Boards", action = "Topic", slug = UrlParameter.Optional, page = UrlParameter.Optional },
615 new { id = @"d+", page = @"d+" },
616 new[] { "Nop.Web.Controllers" });
617 routes.MapLocalizedRoute("ForumWatch",
618 "boards/forumwatch/{id}",
619 new { controller = "Boards", action = "ForumWatch" },
620 new { id = @"d+" },
621 new[] { "Nop.Web.Controllers" });
622 routes.MapLocalizedRoute("ForumRSS",
623 "boards/forumrss/{id}",
624 new { controller = "Boards", action = "ForumRSS" },
625 new { id = @"d+" },
626 new[] { "Nop.Web.Controllers" });
627 routes.MapLocalizedRoute("ForumSlug",
628 "boards/forum/{id}/{slug}",
629 new { controller = "Boards", action = "Forum", slug = UrlParameter.Optional },
630 new { id = @"d+" },
631 new[] { "Nop.Web.Controllers" });
632 routes.MapLocalizedRoute("ForumSlugPaged",
633 "boards/forum/{id}/{slug}/page/{page}",
634 new { controller = "Boards", action = "Forum", slug = UrlParameter.Optional, page = UrlParameter.Optional },
635 new { id = @"d+", page = @"d+" },
636 new[] { "Nop.Web.Controllers" });
637 routes.MapLocalizedRoute("ForumGroupSlug",
638 "boards/forumgroup/{id}/{slug}",
639 new { controller = "Boards", action = "ForumGroup", slug = UrlParameter.Optional },
640 new { id = @"d+" },
641 new[] { "Nop.Web.Controllers" });
642 routes.MapLocalizedRoute("Search",
643 "boards/search",
644 new { controller = "Boards", action = "Search" },
645 new[] { "Nop.Web.Controllers" });
646
647 //private messages
648 routes.MapLocalizedRoute("PrivateMessages",
649 "privatemessages/{tab}",
650 new { controller = "PrivateMessages", action = "Index", tab = UrlParameter.Optional },
651 new[] { "Nop.Web.Controllers" });
652 routes.MapLocalizedRoute("PrivateMessagesPaged",
653 "privatemessages/{tab}/page/{page}",
654 new { controller = "PrivateMessages", action = "Index", tab = UrlParameter.Optional },
655 new { page = @"d+" },
656 new[] { "Nop.Web.Controllers" });
657 routes.MapLocalizedRoute("PrivateMessagesInbox",
658 "inboxupdate",
659 new { controller = "PrivateMessages", action = "InboxUpdate" },
660 new[] { "Nop.Web.Controllers" });
661 routes.MapLocalizedRoute("PrivateMessagesSent",
662 "sentupdate",
663 new { controller = "PrivateMessages", action = "SentUpdate" },
664 new[] { "Nop.Web.Controllers" });
665 routes.MapLocalizedRoute("SendPM",
666 "sendpm/{toCustomerId}",
667 new { controller = "PrivateMessages", action = "SendPM" },
668 new { toCustomerId = @"d+" },
669 new[] { "Nop.Web.Controllers" });
670 routes.MapLocalizedRoute("SendPMReply",
671 "sendpm/{toCustomerId}/{replyToMessageId}",
672 new { controller = "PrivateMessages", action = "SendPM" },
673 new { toCustomerId = @"d+", replyToMessageId = @"d+" },
674 new[] { "Nop.Web.Controllers" });
675 routes.MapLocalizedRoute("ViewPM",
676 "viewpm/{privateMessageId}",
677 new { controller = "PrivateMessages", action = "ViewPM" },
678 new { privateMessageId = @"d+" },
679 new[] { "Nop.Web.Controllers" });
680 routes.MapLocalizedRoute("DeletePM",
681 "deletepm/{privateMessageId}",
682 new { controller = "PrivateMessages", action = "DeletePM" },
683 new { privateMessageId = @"d+" },
684 new[] { "Nop.Web.Controllers" });
685
686 //activate newsletters
687 routes.MapLocalizedRoute("NewsletterActivation",
688 "newsletter/subscriptionactivation/{token}/{active}",
689 new { controller = "Newsletter", action = "SubscriptionActivation" },
690 new { token = new GuidConstraint(false) },
691 new[] { "Nop.Web.Controllers" });
692
693 //robots.txt
694 routes.MapRoute("robots.txt",
695 "robots.txt",
696 new { controller = "Common", action = "RobotsTextFile" },
697 new[] { "Nop.Web.Controllers" });
698
699 //sitemap (XML)
700 routes.MapLocalizedRoute("sitemap.xml",
701 "sitemap.xml",
702 new { controller = "Common", action = "SitemapXml" },
703 new[] { "Nop.Web.Controllers" });
704 routes.MapLocalizedRoute("sitemap-indexed.xml",
705 "sitemap-{Id}.xml",
706 new { controller = "Common", action = "SitemapXml" },
707 new { Id = @"d+" },
708 new[] { "Nop.Web.Controllers" });
709
710 //store closed
711 routes.MapLocalizedRoute("StoreClosed",
712 "storeclosed",
713 new { controller = "Common", action = "StoreClosed" },
714 new[] { "Nop.Web.Controllers" });
715
716 //install
717 routes.MapRoute("Installation",
718 "install",
719 new { controller = "Install", action = "Index" },
720 new[] { "Nop.Web.Controllers" });
721
722 //page not found
723 routes.MapLocalizedRoute("PageNotFound",
724 "page-not-found",
725 new { controller = "Common", action = "PageNotFound" },
726 new[] { "Nop.Web.Controllers" });
727 }
728
729 public int Priority
730 {
731 get
732 {
733 return 0;
734 }
735 }
736 }
737 }
Nop.Web.Framework.Localization.LocalizedRouteExtensions类中对RouteCollection routes添加了MapLocalizedRoute的扩展方法
1 routes.MapLocalizedRoute("HomePage",
2 "",
3 new { controller = "Home", action = "Index" },
4 new[] { "Nop.Web.Controllers" });
Nop.Web.Framework.Localization.LocalizedRoute继承Route类进行扩展多语言Seo的支持
1 public static Route MapLocalizedRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
2 {
3 if (routes == null)
4 {
5 throw new ArgumentNullException("routes");
6 }
7 if (url == null)
8 {
9 throw new ArgumentNullException("url");
10 }
11 //LocalizedRoute 进行了多语言Seo优化
12 var route = new LocalizedRoute(url, new MvcRouteHandler())
13 {
14 Defaults = new RouteValueDictionary(defaults),
15 Constraints = new RouteValueDictionary(constraints),
16 DataTokens = new RouteValueDictionary()
17 };
18
19 if ((namespaces != null) && (namespaces.Length > 0))
20 {
21 route.DataTokens["Namespaces"] = namespaces;
22 }
23
24 routes.Add(name, route);
25
26 return route;
27 }
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/
1 routes.MapLocalizedRoute("HomePage", 2 "", 3 new { controller = "Home", action = "Index" }, 4 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友好链接的路由注册,代码如下:
1 using System.Web.Routing;
2 using Nop.Web.Framework.Localization;
3 using Nop.Web.Framework.Mvc.Routes;
4 using Nop.Web.Framework.Seo;
5
6 namespace Nop.Web.Infrastructure
7 {
8 public partial class GenericUrlRouteProvider : IRouteProvider
9 {
10 public void RegisterRoutes(RouteCollection routes)
11 {
12 //generic URLs
13 routes.MapGenericPathRoute("GenericUrl",
14 "{generic_se_name}",
15 new {controller = "Common", action = "GenericUrl"},
16 new[] {"Nop.Web.Controllers"});
17
18 //define this routes to use in UI views (in case if you want to customize some of them later)
19 routes.MapLocalizedRoute("Product",
20 "{SeName}",
21 new { controller = "Product", action = "ProductDetails" },
22 new[] {"Nop.Web.Controllers"});
23
24 routes.MapLocalizedRoute("Category",
25 "{SeName}",
26 new { controller = "Catalog", action = "Category" },
27 new[] { "Nop.Web.Controllers" });
28
29 routes.MapLocalizedRoute("Manufacturer",
30 "{SeName}",
31 new { controller = "Catalog", action = "Manufacturer" },
32 new[] { "Nop.Web.Controllers" });
33
34 routes.MapLocalizedRoute("Vendor",
35 "{SeName}",
36 new { controller = "Catalog", action = "Vendor" },
37 new[] { "Nop.Web.Controllers" });
38
39 routes.MapLocalizedRoute("NewsItem",
40 "{SeName}",
41 new { controller = "News", action = "NewsItem" },
42 new[] { "Nop.Web.Controllers" });
43
44 routes.MapLocalizedRoute("BlogPost",
45 "{SeName}",
46 new { controller = "Blog", action = "BlogPost" },
47 new[] { "Nop.Web.Controllers" });
48
49 routes.MapLocalizedRoute("Topic",
50 "{SeName}",
51 new { controller = "Topic", action = "TopicDetails" },
52 new[] { "Nop.Web.Controllers" });
53
54
55
56 //the last route. it's used when none of registered routes could be used for the current request
57 //but in this case we cannot process non-registered routes (/controller/action)
58 //routes.MapLocalizedRoute(
59 // "PageNotFound-Wildchar",
60 // "{*url}",
61 // new { controller = "Common", action = "PageNotFound" },
62 // new[] { "Nop.Web.Controllers" });
63 }
64
65 public int Priority
66 {
67 get
68 {
69 //it should be the last route
70 //we do not set it to -int.MaxValue so it could be overridden (if required)
71 return -1000000;
72 }
73 }
74 }
75 }
76
我们发现多了MapGenericPathRoute的扩展
1 //generic URLs
2 routes.MapGenericPathRoute("GenericUrl",
3 "{generic_se_name}",
4 new {controller = "Common", action = "GenericUrl"},
5 new[] {"Nop.Web.Controllers"});
我们来看看MapGenericPathRoute方法来自Nop.Web.Framework.Seo.GenericPathRouteExtensions类中。
通过Nop.Web.Framework.Seo.GenericPathRoute完成Seo友好链接的路由扩展
1 public static Route MapGenericPathRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
2 {
3 if (routes == null)
4 {
5 throw new ArgumentNullException("routes");
6 }
7 if (url == null)
8 {
9 throw new ArgumentNullException("url");
10 }
11 //GenericPathRoute中实现Seo友好链接路由
12 var route = new GenericPathRoute(url, new MvcRouteHandler())
13 {
14 Defaults = new RouteValueDictionary(defaults),
15 Constraints = new RouteValueDictionary(constraints),
16 DataTokens = new RouteValueDictionary()
17 };
18
19 if ((namespaces != null) && (namespaces.Length > 0))
20 {
21 route.DataTokens["Namespaces"] = namespaces;
22 }
23
24 routes.Add(name, route);
25
26 return route;
27 }
GenericPathRoute对路由进行扩展,只重写了GetRouteData方法用于解析url,帮助路由到正确的执行位置。代码如下:
1 /// <summary>
2 /// Returns information about the requested route.
3 /// </summary>
4 /// <param name="httpContext">An object that encapsulates information about the HTTP request.</param>
5 /// <returns>
6 /// An object that contains the values from the route definition.
7 /// </returns>
8 public override RouteData GetRouteData(HttpContextBase httpContext)
9 {
10 RouteData data = base.GetRouteData(httpContext); return null;
11 if (data != null && DataSettingsHelper.DatabaseIsInstalled())
12 {
13 var urlRecordService = EngineContext.Current.Resolve<IUrlRecordService>();
14 var slug = data.Values["generic_se_name"] as string;
15 //performance optimization.
16 //we load a cached verion here. it reduces number of SQL requests for each page load
17 var urlRecord = urlRecordService.GetBySlugCached(slug);
18 //comment the line above and uncomment the line below in order to disable this performance "workaround"
19 //var urlRecord = urlRecordService.GetBySlug(slug);
20 if (urlRecord == null)
21 {
22 //no URL record found
23
24 //var webHelper = EngineContext.Current.Resolve<IWebHelper>();
25 //var response = httpContext.Response;
26 //response.Status = "302 Found";
27 //response.RedirectLocation = webHelper.GetStoreLocation(false);
28 //response.End();
29 //return null;
30
31 data.Values["controller"] = "Common";
32 data.Values["action"] = "PageNotFound";
33 return data;
34 }
35 //ensure that URL record is active
36 if (!urlRecord.IsActive)
37 {
38 //URL record is not active. let's find the latest one
39 var activeSlug = urlRecordService.GetActiveSlug(urlRecord.EntityId, urlRecord.EntityName, urlRecord.LanguageId);
40 if (string.IsNullOrWhiteSpace(activeSlug))
41 {
42 //no active slug found
43
44 //var webHelper = EngineContext.Current.Resolve<IWebHelper>();
45 //var response = httpContext.Response;
46 //response.Status = "302 Found";
47 //response.RedirectLocation = webHelper.GetStoreLocation(false);
48 //response.End();
49 //return null;
50
51 data.Values["controller"] = "Common";
52 data.Values["action"] = "PageNotFound";
53 return data;
54 }
55
56 //the active one is found
57 var webHelper = EngineContext.Current.Resolve<IWebHelper>();
58 var response = httpContext.Response;
59 response.Status = "301 Moved Permanently";
60 response.RedirectLocation = string.Format("{0}{1}", webHelper.GetStoreLocation(), activeSlug);
61 response.End();
62 return null;
63 }
64
65 //ensure that the slug is the same for the current language
66 //otherwise, it can cause some issues when customers choose a new language but a slug stays the same
67 var workContext = EngineContext.Current.Resolve<IWorkContext>();
68 var slugForCurrentLanguage = SeoExtensions.GetSeName(urlRecord.EntityId, urlRecord.EntityName, workContext.WorkingLanguage.Id);
69 if (!String.IsNullOrEmpty(slugForCurrentLanguage) &&
70 !slugForCurrentLanguage.Equals(slug, StringComparison.InvariantCultureIgnoreCase))
71 {
72 //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)
73 var webHelper = EngineContext.Current.Resolve<IWebHelper>();
74 var response = httpContext.Response;
75 //response.Status = "302 Found";
76 response.Status = "302 Moved Temporarily";
77 response.RedirectLocation = string.Format("{0}{1}", webHelper.GetStoreLocation(), slugForCurrentLanguage);
78 response.End();
79 return null;
80 }
81
82 //process URL
83 switch (urlRecord.EntityName.ToLowerInvariant())
84 {
85 case "product":
86 {
87 data.Values["controller"] = "Product";
88 data.Values["action"] = "ProductDetails";
89 data.Values["productid"] = urlRecord.EntityId;
90 data.Values["SeName"] = urlRecord.Slug;
91 }
92 break;
93 case "category":
94 {
95 data.Values["controller"] = "Catalog";
96 data.Values["action"] = "Category";
97 data.Values["categoryid"] = urlRecord.EntityId;
98 data.Values["SeName"] = urlRecord.Slug;
99 }
100 break;
101 case "manufacturer":
102 {
103 data.Values["controller"] = "Catalog";
104 data.Values["action"] = "Manufacturer";
105 data.Values["manufacturerid"] = urlRecord.EntityId;
106 data.Values["SeName"] = urlRecord.Slug;
107 }
108 break;
109 case "vendor":
110 {
111 data.Values["controller"] = "Catalog";
112 data.Values["action"] = "Vendor";
113 data.Values["vendorid"] = urlRecord.EntityId;
114 data.Values["SeName"] = urlRecord.Slug;
115 }
116 break;
117 case "newsitem":
118 {
119 data.Values["controller"] = "News";
120 data.Values["action"] = "NewsItem";
121 data.Values["newsItemId"] = urlRecord.EntityId;
122 data.Values["SeName"] = urlRecord.Slug;
123 }
124 break;
125 case "blogpost":
126 {
127 data.Values["controller"] = "Blog";
128 data.Values["action"] = "BlogPost";
129 data.Values["blogPostId"] = urlRecord.EntityId;
130 data.Values["SeName"] = urlRecord.Slug;
131 }
132 break;
133 case "topic":
134 {
135 data.Values["controller"] = "Topic";
136 data.Values["action"] = "TopicDetails";
137 data.Values["topicId"] = urlRecord.EntityId;
138 data.Values["SeName"] = urlRecord.Slug;
139 }
140 break;
141 default:
142 {
143 //no record found
144
145 //generate an event this way developers could insert their own types
146 EngineContext.Current.Resolve<IEventPublisher>()
147 .Publish(new CustomUrlRecordEntityNameRequested(data, urlRecord));
148 }
149 break;
150 }
151 }
152 return data;
153 }
路由匹配到支持Seo友好链接会重定向链接新的路由位置,nop 3.9目前支持下图中的路由
提示:数据库UrlRecord表对Seo友情链接提供支持。
四.总结
1.继承IRouteProvider对路由进行扩展。(注意路由注册顺序,ASP.NET MVC 只匹配第一次成功的路由信息)
2.多语言Seo友好链接通过LocalizedRoute类进行路由扩展(MapLocalizedRoute扩展中调用)
3.页面Seo友好链接通过GenericPathRoute类进行路由扩展(MapGenericPathRoute扩展中调用)