How to change the default URL rewriting (SEO Friendly URLs) and use them in your site
Jun/14/2010
With V4 of the Tradepoint ecommerce engine, the custom page routing engine
has been removed in favour of ASP.NET V4 Routing. This allows for a 200% speed
increase and for easier customization of the routes in your site so that you can
customize how products, categories or any other page will be displayed on your
site.
As a reminder you can download the base ecommerce site from: http://www.tradepoint360.com/ecommerce/demo.tradepoint360.com.zip
All custom routes are stored in the global.asax file in the root of your
site. You will see the following default entries in the application_start
event:
void Application_Start(object sender, EventArgs e)
{
//Note: Physical
pages that exist will be ignored by default and displayed as
normal.
System.Web.Routing.
RouteTable.Routes.Ignore("{resource}.axd/{*pathInfo}");
System.Web.Routing.
RouteTable.Routes.Ignore("{resource}.svc/{*pathInfo}");
System.Web.Routing.
RouteTable.Routes.Ignore("devexpress/{*pathInfo}");
System.Web.Routing.
RouteTable.Routes.Ignore("{resource}.js");
System.Web.Routing.
RouteTable.Routes.Ignore("styles/{*pathInfo}");
System.Web.Routing.
RouteTable.Routes.Ignore("templates/{*pathInfo}");
System.Web.Routing.
RouteTable.Routes.Ignore("app_themes/{*pathInfo}");
//These are order
specific from most specific to least.
//Image
rewrites
System.Web.Routing.
RouteTable.Routes.Add("ProductDefaultImage", new System.Web.Routing.Route("images/products/{Model}/default.png", new Tradepoint.Enterprise.Web.Rewritting.TradepointImageRouteHandler(true, false)));
System.Web.Routing.
RouteTable.Routes.Add("ProductThumbnailImage", new System.Web.Routing.Route("images/products/{Model}/thumbnail.png", new Tradepoint.Enterprise.Web.Rewritting.TradepointImageRouteHandler(true, true)));
System.Web.Routing.
RouteTable.Routes.Add("ProductPhoto", new System.Web.Routing.Route("images/products/{Model}/photos/{Caption}.png", new Tradepoint.Enterprise.Web.Rewritting.TradepointImageRouteHandler(false, false)));
System.Web.Routing.
RouteTable.Routes.Add("ProductPhotoThumbnail", new System.Web.Routing.Route("images/products/{Model}/thumbnails/{Caption}.png", new Tradepoint.Enterprise.Web.Rewritting.TradepointImageRouteHandler(false, true)));
//Other
System.Web.Routing.
RouteTable.Routes.Add("ProductTypeModel", new System.Web.Routing.Route("products/{Model}/{Type}", new System.Web.Routing.PageRouteHandler("~/products/productpages/default.aspx")));
System.Web.Routing.
RouteTable.Routes.Add("ProductModel", new System.Web.Routing.Route("products/{Model}", new System.Web.Routing.PageRouteHandler("~/products/productpages/default.aspx")));
System.Web.Routing.
RouteTable.Routes.Add("PrimaryCategoryID", new System.Web.Routing.Route("categories/ID={ID}", new System.Web.Routing.PageRouteHandler("~/products/category.aspx")));
System.Web.Routing.
RouteTable.Routes.Add("PrimaryCategoryName", new System.Web.Routing.Route("categories/{Name}", new System.Web.Routing.PageRouteHandler("~/products/category.aspx")));
System.Web.Routing.
RouteTable.Routes.Add("GroupID", new System.Web.Routing.Route("groups/ID={ID}", new System.Web.Routing.PageRouteHandler("~/products/group.aspx")));
System.Web.Routing.
RouteTable.Routes.Add("DepartmentID", new System.Web.Routing.Route("departments/ID={ID}", new System.Web.Routing.PageRouteHandler("~/products/department.aspx")));
System.Web.Routing.
RouteTable.Routes.Add("GroupName", new System.Web.Routing.Route("groups/{Name}", new System.Web.Routing.PageRouteHandler("~/products/group.aspx")));
System.Web.Routing.
RouteTable.Routes.Add("DepartmentName", new System.Web.Routing.Route("departments/{Name}", new System.Web.Routing.PageRouteHandler("~/products/department.aspx")));
//Add generic
handler for all arbitrary urls if all else doesn't line
up.
System.Web.Routing.
RouteTable.Routes.Add("Generic", new System.Web.Routing.Route("{*URL}", new
Tradepoint.Enterprise.Web.Rewritting.TradepointRouteHandler()));
}
You'll note that there is a section for exceptions. These are URLs that you
don't want it to map. It will ignore any mappings that might have been
followed.
The rest go from most specific to least, finally getting to "all" which then
processes any custom URLs that have no matching pattern that you might have for
products or categories etc.
Note that the first part of the System.Web.Routing.RouteTable.Routes.Add
method is the name of the rewritting rule. This is important, because you will
use this to construct your URL. The following example shows how to construct a
URL based on a product image:
The Route:
System.Web.Routing.RouteTable.Routes.Add("ProductDefaultImage", new System.Web.Routing.Route("images/products/{Model}/default.png", new Tradepoint.Enterprise.Web.Rewritting.TradepointImageRouteHandler(true, false)));
We have a route named ProductDefaultImage that we're going to reference. The
route is "images/products/{Model}/default.png". "{Model}" references a variable.
That is it could be anything in that spot in the URL. In this case, {Model} is
the Product Model. Thus as an example the following URL would match this route:
/images/products/ACM-121/default.png". The system will then find ACM-121
as the model and look it up in the database automatically and display the
default image for that model. (note that there is a separate route for the
thumbnail version.)
While you could just concatenate a path in your code that references this
URL, the better approach is to reference the name of the route and have the
system go and build it for you. That way, if you ever change the route, the
system will automatically change all of the URLs that reference the named
route.
To reference the route and have it build it for you we'll use code like
this:
<
img src="<%#GetRouteUrl("ProductDefaultImage", new { Model = Eval("Model")
}) %>"/>
What this does is set the src of the image control to the RouteURL.
GetRouteURL is a standard ASP.net function that takes the name of the route as
the first parameter and the second parmeter is a variant named list of the
variables in the route. In this case our {Model} variable is set to the
Evaluated value from the databinding, but it could just as easily be taken from
the product object or any other variable you desire. You can have routes with
multiple variables and simply add a comma after the first variable and add the
second one in the same manner.
The added benefit of this approach is that the parmeters are NOT passed as
request.QueryString items. They are stored in the Page.RouteData.Values
collection. You'll see in the /products/category.aspx.cs how we use these to get
the correct variables and lookup the category in question:
DataContext.CategorySet.MergeOption = System.Data.Objects.
MergeOption.NoTracking;
System.Linq.
IQueryable<Category> CategoryList = null;
if (RouteData.Values.ContainsKey("ID")) {
Guid ID = new Guid(RouteData.Values["ID"].ToString());
CategoryList =
from c in DataContext.CategorySet where c.ID == ID select c;
}
else if (RouteData.Values.ContainsKey("Name")) {
string Name = RouteData.Values["Name"].ToString().Replace("+", " ").Replace("_", "
");
CategoryList =
from c in DataContext.CategorySet where c.Name == Name select c;
}
else if (Request.QueryString["ID"] != null) {
Guid ID = new Guid(Request.QueryString["ID"].ToString());
CategoryList =
from c in DataContext.CategorySet where c.ID == ID select c;
}
category = CategoryList.Where(c => c.CategoryType ==
12).FirstOrDefault();
if (category == null) {
Response.Redirect(
"/securityerror.aspx");
return;
}
Note how we always test to see if the object equals null and
redirect to our security error page. By doing so we eliminate a possible ASP.NET
error showing up to the end user and we protect the site from hacking. This
combined with Linq to Entities prevents sql injection from occuring. (The final
if statement is used for backwards compatibility only.)
By using the routing system built into V4 of the Tradepoint Ecommerce system
you can create any rules that you wish to create fully SEO friendly URLs. There
are a couple of "gotchas" that you'll want to note:
- Always apply your rules from most specific to least or your route will
never be processed.
- Always test your pages to make sure that other routes are unintentionally
matching a route that you've created. You may have to change it as a result of
your testing.
- Always use the GetRouteURL function whenever possible. The only two times
we do not use these are for Product and Category URLs. The reason for this is
because they have generic URL support so ANYTHING could match so we create
specific ones.
- Please see here: http://weblogs.asp.net/scottgu/archive/2009/10/13/url-routing-with-asp-net-4-web-forms-vs-2010-and-net-4-0-series.aspx for
a good getting started guide to URL rewritting in .NET.
- Always use dashes (-) to replace spaces both when encoding and when
decoding your URLs otherwise some browsers may not work and your SEO
optimizations may be compromised.