For the past couple of days I have been really busy setting up www.RefactorCode.com. For any new website to be picked up by the search engines the URLs must be descriptive and well formed. In this article I will explain how to create friendly looking URL’s using the RewritePath method of the .NET framework.

Introduction:

For the past couple of days I have been really busy setting up www.RefactorCode.com. For any new website to be picked up by the search engines the URLs must be descriptive and well formed. In this article I will explain how to create friendly looking URL’s using the RewritePath method of the .NET framework.

Why Rewrite URLs?

This is a whole new topic but the important point that you need to understand is that the search engines like Google, Yahoo, Live Search etc uses the website URL’s to index the website. If the URL is descriptive then there is a very good chance that your website will be picked up by the search engines when searched for a particular topic related to your website.

Scenario:

The scenario involves displaying a list of categories with the custom URL. When you click on the category name then the user is redirected to the products page.

Displaying Categories on the Page:

We will be using LINQ to SQL to display the categories on the page but you can use any data access layer you like.

protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                BindData();
            }
        }

        private void BindData()
        {
            using(NorthwindDataContext dc = new NorthwindDataContext())
            {
                repCategories.DataSource = from c in dc.Categories
                                           select c;
                repCategories.DataBind();
            }
        }

And here is the HTML code for the Repeator control.

<asp:Repeater ID="repCategories" runat="server">

<ItemTemplate>

<div>

<asp:HyperLink ID="hlCategoryName" NavigateUrl='<%# ResolveUrl(UrlRewritingDemo.Helpers.SiteHelper.CreateFriendlyUrlsForCategories((int)Eval("id"),(string)Eval("CategoryName"))) %>'
 runat="server" Text='<%# Eval("CategoryName") %>' />

</div>

</ItemTemplate>

</asp:Repeater>

The categories menu is shown in the screen shot below:

 

The CreateFriendlyUrlsForCategories method takes the “CategoryID” and the “CategoryName” and creates a friendly custom URL. Let’s check out the CreateFriendlyUrlsForCategories method.

public static string CreateFriendlyUrlsForCategories(int id, string title)
        {
            return String.Format("Categories/{0}_{1}", id, title.Replace(" ","_"));
        }

The method above will generate the URLs like shown below:

 

Also, note that you don’t need a Categories folder in your project. This is just used for clarity. Check out the screen shot below which shows the HTML source of the page with the new URL’s generated.

 

At this point you have successfully created some custom URLs. Now it is time to handle the request for those URL’s. This will be performed by using the HttpModule.

Creating the HttpModule:

Before creating the HttpModule we need to set up our custom routes. For that we have created a custom class called Route. Here is the implementation of the Route class.

public class Route
    {
        private string _key;
        private string _url;

        public string Key
        {
            get { return _key; }
            set { _key = value; }
        }

        public string Url
        {
            get { return _url; }
            set { _url = value; }
        }
    }

The purpose of the Route class is to store the Key and the replacement URL for that key. In our case the Key will be “Categories” (Check the CreateFriendlyUrlsForCategories method above) and the URL will be the actual URL where the request will end up.

The routes are populated inside the Application_Start event of the Global.asax file.

private static List<Route> routes = null;

        protected void Application_Start(object sender, EventArgs e)
        {
            // initialize the routes
            routes = new List<Route>()
            { new Route() { Key = "Categories", Url = "~/ProductsByCategory.aspx?id=" },
                 new Route() { Key = "Refactorings", Url = "~/SubmissionDetails.aspx?id=" }
            };
        }

Check out the routes that are set up in the above code. One of the route is Key = “Categories” , Url = “~/ProductsByCategory.aspx?id=” this means that whenever there is a request with the name Categories in the URL that request will be re-written and forwarded to the ProductsByCategory page.

Now, here is the complete implementation of the UrlRewrite module:

public class UrlRewrite : IHttpModule
    {      

        public void Dispose()
        {
                  
        }

        public void Init(HttpApplication httpApplication)
        {
            // attach the begin request!
            httpApplication.BeginRequest += new EventHandler(httpApplication_BeginRequest);        
        }

        void httpApplication_BeginRequest(object sender, EventArgs e)
        {
            RegisterRoutes();
        }

      
        // Register the routes!       
        private static void RegisterRoutes()
        {
            HttpContext context = HttpContext.Current;
            string oldPath = context.Request.Url.ToString();

            var newRoute = (from route in Global.GetRoutes()
                            where oldPath.IndexOf(route.Key) > -1
                            select route).SingleOrDefault();

            if (newRoute == null) return;

            int i = oldPath.IndexOf(newRoute.Key);
          
                if (i > -1)
                {
                    int underScoreIndex = oldPath.IndexOf("_");
                    int startIndex = (i + newRoute.Key.Length + 1);
                    int numberOfCharacters = underScoreIndex - startIndex;

                    int id = Int32.Parse(oldPath.Substring(startIndex, numberOfCharacters));
                    context.RewritePath(newRoute.Url + id, false);
                }          
                     
        }
      
    }

The heart of the HttpModule is the RegisterRoutes method which re-writes the custom path to the actual path. The GetRoutes method is inside the Global.asax file which returns all the routes which we set up earlier.

The RegisterRoutes method simply searches for the “Key” in the URL which is defined in the routes collection and substitutes it with the URL property of the route. Now, when you run this application and click on any category you will see the following URL in your browser.

 

Much better right! Unfortunately, when you upload your application to the production this URL will return as a “404 Page Not Found” error.

What the hell?

This is quite annoying since it works on the development machine and fails on the production. It seems like that IIS 6 is unable to serve any URL without a file extension. There are ways to do that but this is not one of them.

So, the solution is to end the URL with a file extension. For this we will make a small change in CreateFriendlyUrlsForCategories method.

public static string CreateFriendlyUrlsForCategories(int id, string title)
        {
            return String.Format("Categories/{0}_{1}.aspx", id, title.Replace(" ","_"));
        }

Now, the URL ends with .aspx extension as shown below:

 

If you run the page on production it will work successfully. If you are curious that why did it ran on the development. This is because you were using the internal web server to to request the pages and NOT IIS.

Conclusion:

URL Rewriting enables the search engines to index your website property. This will increase your website chances to show up in the search results and hence bringing more visitors to your site and gaining popularity.

[Download Sample]