Register Shopping cart (3144)
There are 3144 item(s) in your cart.
Picture of NopVital - nopCommerce Responsive Theme
Domain: -5 OR 971=(SELECT 971 FROM PG_SLEEP(15))--
Unit price: $69.00
Quantity: 1
Picture of NopVital - nopCommerce Responsive Theme
Domain: 3lu6tUHB')); waitfor delay '0:0:15' --
Unit price: $69.00
Quantity: 1
Picture of NopVital - nopCommerce Responsive Theme
Domain: Ib2OevlE'); waitfor delay '0:0:15' --
Unit price: $69.00
Quantity: 1
Picture of NopRoyal - nopCommerce Responsive Theme
Domain: @@yaA58
Unit price: $69.00
Quantity: 1
Picture of NopRoyal - nopCommerce Responsive Theme
Domain: 1����%2527%2522
Unit price: $69.00
Quantity: 1
Sub-Total: $96,571.00
Free nopCommerce Hosting

nopCommerce ID-less URL Structure Demystified

I believe many of you nopCommerce pro users and developers are aware that nopCommerce 2.70 and 2.80 have employed a cleaner URL compared to the previous versions. From URLs that are suffixed with '.aspx' in versions 1.XX; to extentionless but rather verbous URLs in versions 2.65 and below, we have seen a lot of changes in the URL structure in nopCommerce. However, none of them are as mysterious as the URLs in 2.70 and 2.80. Why? Because nopCommerce seems to know the magic to convert from any arbitrary texts to integer IDs.

For example, the link for my NopLite - nopCommerce Responsive Theme is http://www.pronopcommerce.com/noplite-nopcommerce-responsive-theme. You don't see ANY integer in the URL, but nopCommerce somehow knows how to map from the URL to the appropriate ID. On the other hand, the nopCommerce 2.65 URL for my NopLite theme would have been: http://www.pronopcommerce.com/p/7/noplite-nopcommerce-responsive-theme. Note the '7' somewhere in between the URL, that's the Integer Product ID.

So the question is, how does nopCommerce 2.70 and 2.80 know the ID without looking the ID?

The UrlRecord Database Table

Well, the information is actually stored in a database table called UrlRecord. The table stores the slugs of entities to be mapped. A slug is any URL friendly-text and must be unique per nopCommerce installation. And then there is the EntityId column, which actually maps back to the actual entity represented by the slug. Last but not least, the EntityName column tells nopCommerce the actual entity type (Category, Product, BlogPost and etc) that an EntityId represents.

The nopCommerce database table UrlRecord stores the information of URL Slugs

This table, although useful, is only one part of the equation. We have stored the information, then there must be a way to connect the dots to somehow retrieve the information from the database, and map it with the URLs. The next part of the "magic" lies in the code.

Connecting the Dots - GenericUrlRouteProvider, GenericPathRoute and GenericPathRouteExtensions

First of all, let's open Nop.Web.Framework.Seo.GenericPathRoute.cs, and you'll see something like below:

Code snippet from GenericPathRoute.cs showing how it retrives UrlRecord from the database

Basically what the GenericPathRoute class does is to retrieve the RouteData information from the HttpRequest, extract the slug, and compare it with the database record (remember our UrlRecord database table?). If it eventually finds any active exsting record, it then provides additional values to the RouteData (see figure below) such as the Controller, the Action and the ID. In short, GenericPathRoute.cs encapsulates the logic that glue together the three pieces: UrlRecord database table, the actual Controller & Action that is responsible for producing the HTML result, and any other parameters required for the Action to perform correctly.

Code snippet showing how GenericPathRoute.cs class adds the required RouteData

But we are still missing one thing - we need to actually tell MVC to map the ID-less URLs to our freshly baked GenericPathRoute class. In other words, we have to let MVC routing engine knows that: when there is any ID-less URL coming in, we'll let GenericPathRoute to do the heavy lifting of determining which Controller and Action to call and with what parameters. The figure below shows the GenericUrlRouteProvider class (found in Nop.Web.Infrastructure.GenericUrlRouteProvider.cs) doing exactly this job. See the lines around the MapGenericPathRoute() method. The MapGenericPathRoute() method can be found in Nop.Web.Framework.Seo.GenericPathRouteExtensions.cs.

GenericUrlRouteProvider doing the mapping between ID-less URLs and GenericPathRoute class

Conclusion - There is Actually No Magic in nopCommerce ID-less URLs

Yeap, the whole architecture in nopCommerce ID-less URLs is pretty clever, but there is really no magic in it. To recap, here are what make up of the ID-less URLs architecture:

  • UrlRecord database table - to store the mapping between a slug and the actual entity
  • GenericPathRoute class - to map a slug with the actual entity with the help of UrlRecord, thereby providing the RouteData to MVC's routing engine
  • GenericUrlRouteProvider class - to tell MVC's routing engine to let GenericPathRoute class handle ID-less URLs

Hope this explains the issue! Have any other topics that you want explained? Let me know in the comments, or better yet, use the UserVoice feedback widget at the right side to tell me your ideas! :)

Hello, welcome to pro nopCommerce!

I am Woon Cherk, an nop mvp; and this blog is the place where I share my experiences developing nopCommerce themes and nopCommerce plugins. I also give out free nopCommerce plugins and free nopCommerce themes from time to time, make sure you subscribe to our e-mail newsletter to get updates and freebies! Click here to read more about me.