Monday, August 30, 2010

Url Rewriting Made Easy - Article 2

Before I explain what is URL Rewriting and how simple it is to go for, kindly ready my previous article on same topic i.e. Url Rewriting Made Easy - Article 1, in this article I have explained some basics about URL Rewriting.
The very obvious question rises is, why should you go for URL Rewriting?
Why Should I go For rewriting URL?
  • Short URLs
  • easy to type & easy to remember URLs
  • Site Structure is Visible through URLs
  • URLs are precise and which won't change
  • URLs WHICH MAKE SENSE for End USER as well as for SEO purpose
Now if we look at above points as a web developer then we are very sure that these all things prone to change, we need to pass parameters and change directory structure of the web application. If the web application is very huge then managing above things is a headache.
Lets discuss the case of online shopping; "As a developer for e-commerce application or for online super market projects"; as per above bullet points we need separate URL for followings
  • Every Product should be identified separately
  • Category as well as subcategory should have separate identity
  • Price Range wise
  • Miscellaneous
  • Etc...
Basically one should be able to maintain and manage URLs as per requirement and need.
Now there is a trick, we have to show one URL on browser and internally use different url. Yes we are showing something which is readable and easy; which satisfies all bullet points listed above, but internally we can manage our logic our way rather in traditional way... 
e.g. we are displaying url say "http://www.mysite.com/product1" and we can internally use url "http://www.mysite.com/default.aspx?category=product1" its quite logical.Now the ultimate question arises is how to achieve this? So lets start building the logic. Check out following diagram, it is a logical flow of URL Rewrite operation.


So the very important thing we need to rewrite url is HTTP handlers, but before developing this handler we need to decide what URLs we should build for web application. For this Demo Code we will develop URLs which will be either Datewise as well as Product Namewise or show all products. Lets Consider we have following URLs which internally redirects to Default.aspx page with query string.
  • http://www.yoursite.com/PRODUCTS.aspx
  • http://www.yoursite.com/2010.aspx
  • http://www.yoursite.com/2010/08.aspx
  • http://www.yoursite.com/2010/08/24.aspx
Above links will be internally redirect to Default page in respective order as
  • http://www.yoursite.com/default.aspx?products=allproducts
  • http://www.yoursite.com/dafault.aspx?year=2010
  • http://www.yoursite.com/dafault.aspx?year=2010&month=08
  • http://www.yoursite.com/default.aspx?date=2010-08-24
So Rewriting of URL gives us flexibility of having different URLs for different kind of query string, in above example we have four different URLs pointing toward same page. I think this is the beautiful part, search engines see four different, readable URLs but we can handle data in traditional way. (For easy Reference check out DemoCode)
This URL module is same as Article 1 but RewriteModule.cs file is added. Function of PostBack.Browsers is similar i.e. to maintain URL during postbacks.
Now we will develope Code for RewriteModule.cs, this class file implement IHttpModule interface, whose code is as below.
namespace Rewrite
{
    public class RewriteModule : IHttpModule
    {
        public RewriteModule()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        #region IHttpModule Members

        public void Dispose()
        {
            //  throw new NotImplementedException();
        }

        public void Init(HttpApplication context)
        {
            //   throw new NotImplementedException();
            context.BeginRequest += new EventHandler(RewriteModule_BeginRequest);

        }

        void RewriteModule_BeginRequest(object sender, EventArgs e)
        {
            // throw new NotImplementedException();
            HttpContext context = ((HttpApplication)sender).Context;
            string path = context.Request.Path.ToUpperInvariant();
            string url = context.Request.RawUrl.ToUpperInvariant();

            path = path.Replace(".ASPX.CS", "");
            url = url.Replace(".ASPX.CS", "");

            if (url.Contains("/PRODUCTS/"))
            {
                RewriteProduct(context);
            }
            else RewriteDefault(context);
        }

        #endregion

        private static readonly Regex YEAR = new Regex("/([0-9][0-9][0-9][0-9])", RegexOptions.IgnoreCase | RegexOptions.Compiled);
        private static readonly Regex YEAR_MONTH = new Regex("/([0-9][0-9][0-9][0-9])/([0-1][0-9])", RegexOptions.IgnoreCase | RegexOptions.Compiled);
        private static readonly Regex YEAR_MONTH_DAY = new Regex("/([0-9][0-9][0-9][0-9])/([0-1][0-9])/([0-3][0-9])", RegexOptions.IgnoreCase | RegexOptions.Compled);

        private void RewriteDefault(HttpContext context)
        {
            string url = context.Request.RawUrl;

            if (YEAR_MONTH_DAY.IsMatch(url))
            {
                Match match = YEAR_MONTH_DAY.Match(url);
                string year = match.Groups[1].Value;
                string month = match.Groups[2].Value;
                string day = match.Groups[3].Value;
                string date = year + "-" + month + "-" + day;
                context.RewritePath(@"~/default.aspx?date=" + date, false);
            }
            else if (YEAR_MONTH.IsMatch(url))
            {
                Match match = YEAR_MONTH.Match(url);
                string year = match.Groups[1].Value;
                string month = match.Groups[2].Value;
                string path = string.Format("default.aspx?year={0}&month={1}", year, month);
                context.RewritePath(@"~/" + path, false);
            }
            else if (YEAR.IsMatch(url))
            {
                Match match = YEAR.Match(url);
                string year = match.Groups[1].Value;
                string path = string.Format("default.aspx?year={0}", year);
                context.RewritePath(@"~/" + path, false);
            }
            else
            {
                context.RewritePath(url.Replace("Default.aspx", "default.aspx")); 
            }

        }

        private void RewriteProduct(HttpContext context)
        {
            string path = "default.aspx?products=allproducts";
            context.RewritePath(@"~/" + path, false);
        }
    }
}
Lets understand this code first. We have divided logic in two functions, one handles Dates using Regular Expression and other simple handles Product category. i.e. RewriteDefault and RewriteProduct functions. Both functions can be static. To rewrite URL we are using context.RewritePath function, and we have added one eventHandler for context.BeginRequest; i.e.RewriteModule_BeginRequest. Here I assume that the reader is aware about implementation of HTTPMODULE.
After this we will develop index.aspx page, which looks like following
HTML side of Index.aspx is as below
Please Refer HTML of index.aspx, due to limitation of HTML rendering and Blogger compatibility I am not able to display code here. (Please refer html of index.aspx, check demo code.)
index.aspx page uses four URLs discussed above and redirects it self Default.aspx. Now HTML of Default.aspx and Code Behind are as below
This is Default.aspx Page
Here you can see Messages

Code Behind is as below
protected void Page_Load(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(Request.QueryString["date"])) 
        {
            //Write your logic Instead of following code.
            message.InnerText = "Logic For fetching data as per Date: " + Request.QueryString["date"];
        }
        else if (!string.IsNullOrEmpty(Request.QueryString["year"]) && !string.IsNullOrEmpty(Request.QueryString["month"])) 
        {
            //Write your logic Instead of following code.
            message.InnerText = "Logic For fetching data as per Year and Month: " + Request.QueryString["year"] + "/" + Request.QueryString["month"];
        }
        else if (!string.IsNullOrEmpty(Request.QueryString["year"])) 
        {
            //Write your logic Instead of following code.
            message.InnerText = "Logic For fetching data as per Year: " + Request.QueryString["year"];
        }
        else if (!string.IsNullOrEmpty(Request.QueryString["products"])) 
        {
            //Write your logic Instead of following code.
            message.InnerText = "Logic For fetching data for all Products: " + Request.QueryString["products"];
        }
    }
In web.config file (as per HTTPMODULE implementation) we add following code


We are done with our rewrite url logic, for handling Postbacks check out the logic written in "HandlePostBacks.cs" and "PostBack.browser" files. This logic is already explained in Url Rewriting Made Easy - Article 1
Download Demo codes
Submit this story to DotNetKicks

3 comments:

Rhony September 6, 2010 at 12:56 PM  

thanks for the information

Rajesh2210 November 30, 2011 at 1:27 PM  

I really like this artical.....
It is really helpful......
I have tried this.........######Gr8

Ashvin June 20, 2012 at 3:42 PM  

its not woring on server...