Tuesday, June 17, 2008

Perfect ASP.NET application, Part 7

Url rewriting.

Nowdays, every web developer bows to Google and Yahoo. And that is right. Who wants/needs to spend it's time and get the perfect application out there and no visitors. And the only "free" way to get visitors is to get them from search engines. But for that you must make your website easy to spider. You can say anything you want about Google and Yahoo is being able to spider urls with query parameters but show me the popular search phrase where URLs with ? symbols shows up on a first page. Sorry, I did not see that.

So now we want to make our pages to look pretty. So instead of http://www.mspiercing.com/Product.aspx?id=1526 we want something like http://www.mspiercing.com/Product/belly-button-ring-web-ball-1526.aspx

Note: With the current version of IIS 6.0 in order to have a pure .NET UrlRewriting solution you need to have .aspx extension. That is how IIS knows that this request needs to be routed through ASP.NET engine. IIS 7.0 will change that but as of now it's in beta. So it will be hard to rewrite url like that http://www.mspiercing.com/belly-rings simply because there is no .aspx extension. You might use ISAPI dll to do that. But there are no pure "good" .NET solution. Some people use custom 404 page that has .aspx extension to trick IIS. So when request is made for http://www.mspiercing.com/belly-rings IIS trying to serve custom 404.aspx page and that is when .NET kicks in. Not sure that it's good solution because that 404.aspx will be called for everything, even images.

HttpContext.RewritePath makes it really easy in .NET to rewrite the path, but unfortunately it's not that simple. Problem is in the <form runat=server> tag which is a must in .NET

Let say you have rewritten url so when browser hits page http://www.mspiercing.com/Product/belly-button-ring-web-ball-1526.aspx it actually goes to page http://www.mspiercing.com/product.aspx?id=1526. Unfortunately the <form> tag in the HTML will look like following <form method="POST" action="http://www.mspiercing.com/product.aspx?id=1526" ...> and if you have single button on the page when user clicks it the browser going to hit not rewritten url with method POST. And users obviously will see it in their address bar of the browsers. Not good.

I've seem solution that uses custom written object derived from HtmlForm which suppresses rendering of the "action" property. Unfortunately it does not work well with validators and who knows what else. I found a nice easy solution (on internet) . All you need to do is rewrite URL back before Render method called.

So here is a skeleton of application that supports URL rewriting.

Global.asax

-----------------

void Application_BeginRequest(Object sender, EventArgs e)
{
   
HttpContext ctx = HttpContext.Current;
   
string sPath = ctx.Request.Path.ToLower().Substring(clsGlobal._sRoot.Length);
   
int iIndex = sPath.LastIndexOf(".aspx");
   
if (iIndex == -1)
   
     return;
   
ctx.Items["OriginalPath"] = sPath;
   
......do rewriting here....
}

------------------------------

clsStandardPage.cs (remember from previous post that every page is derived form this object)

public class clsStandardPage : System.Web.UI.Page
{
   
protected override void OnPreInit(EventArgs e)
   
{
        //rewrite URL back
        string sPath = (string)Context.Items["OriginalPath"];
        Context.RewritePath(sPath,
null, null);
        ....
     }
}

Note: We need to check that we have request for .aspx file first. We do not want to rewrite request for famous WebResource.axd. Cause it will be routed through ASP.NET since .axd extension assigned to ASP.NET and it will trigger the BeginRequest event.

That solves all problems....

Note: Just discovered that if you use button with PostBackUrl set then it breaks UrlRewriting all together. Do not know exactly why. It has something to do with actual url not matching requested url. But i do not use PostBackUrl in my projects. So far did not have a need. So i am good :)

 

No comments: