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

Read more...

Sunday, August 22, 2010

Url Rewriting Made Easy - Article 1

Rewriting of URL is one of the Major point in terms of Search Engine Optimization as well as in terms of Readability and usability. 
We should go for rewriting of URL because of following reasons
  1. Search Engines Recognizes different URLs.
  2. Same Page can be executed with different unique URLs.
  3. We can also introduce our keywords in the URLs.
In this article we will discuss very preliminary way to generate unique URL which internally can target to same page. We will also discuss how to maintain the URL during postbacks. We have to consider postback case because though we manage to redirect to some URL which is rewritten, during postbacks it again changes to the actual URL.
 
In general Our URLs looks like below
http://www.yourSite.com/products.aspx?category=Kids
http://www.yourSite.com/products.aspx?category=Science
http://www.yourSite.com/products.aspx?category=Biology
We are going to use a less used feature of Asp.Net i.e. "Request.PathInfo" using which we will able handle
URLs which are as below
http://www.yourSite.com/products.aspx/Kids
http://www.yourSite.com/products.aspx/Science
http://www.yourSite.com/products.aspx/Biology
This is one of the simplest approach to Handle URL Rewriting.  Now Lets start implementing it, before we go ahead check out the folder structure of the DemoCode for this article which is as below.
Lets Discuss one File at a time
  • Default.aspx: This file contains simple Div Tag with 3 links point to Product.aspx with desired URLs. Check out following code for the same. Check out URLs.



  • Product.aspx: HTML of Product.aspx contains following simple code.


    code behind of Product.aspx is as follows








    protected void Page_Load(object sender, EventArgs e)
        {
            switch (Request.PathInfo.Substring(1))
            { 
                case "Kids":
                    Response.Write("This is Kids Section");
                    break;
                case "Science":
                    Response.Write("This is Science Section");
                    break;
                case "Biology":
                    Response.Write("This is Biology Section");
                    break;
            }
    
            Response.Write("
    
    
    Check out the URL");
        }
        protected void Button_Click(object sender, EventArgs e)
        {
            Response.Write("URL is Not Changing After PostBack");
        }

    This is very simple way to implement and understand which category is being called. upto this point code can work very well. But to make sure that on post back also i.e. on button click on Product.aspx page same URL should be maintained, we have to write Control Adapter which we have written in "HandlePostBacks.cs" file
  • HandlePostBacks.cs:  In this file we have overriden Render function of Control Adapter class to rewrite the Raw URL during postbacks. check out following code.











    public class HandlePostBacks : System.Web.UI.Adapters.ControlAdapter
    {
        protected override void Render(HtmlTextWriter writer)
        {
            base.Render(new RewriteForPostBack(writer));
        }
    }
    
    public class RewriteForPostBack : System.Web.UI.HtmlTextWriter
    {
        public RewriteForPostBack(HtmlTextWriter writer) : base(writer)
        {
            this.InnerWriter = writer.InnerWriter;
        }
    
        public RewriteForPostBack(System.IO.TextWriter writer) : base(writer)
        {
            base.InnerWriter = writer;
        }
    
        public override void WriteAttribute(string name, string value, bool fEncode)
        {
            if (name == "action")
            {
                HttpContext context;
                context = HttpContext.Current;
                value = context.Request.RawUrl;
                       }
            base.WriteAttribute(name, value, fEncode);
        }
    }

    Now we have almost done with our logic, now what remains is to register above adapter so that it get called automatically. For this we will write logic in "postback.browser" file

  • Postback.browser: We write simple logic which tells .net which adapter control should be used, check following code.



    
        
          
        
      

And we are done with the implementation DOWNLOAD demo code and check it out, very simple and can implement easily on other pages.
Download Demo Code here --> UrlRewitting-Simple Approach 1.zip Submit this story to DotNetKicks

Read more...

Saturday, August 14, 2010

Solved: Could not load type 'System.Web.UI.ScriptReferenceBase'

I came across following error while using AjaxToolKit 3.5, well it is a very common error I think, and I got the solution too.
Could not load type 'System.Web.UI.ScriptReferenceBase' from assembly 'System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.
Above error occurs because of the presence of  asp:ToolkitScriptManager, just replace this toolkitscriptManager with normal asp:ScriptManager
This simply works, no need to get panic about this error :)

Submit this story to DotNetKicks

Read more...