Click here to Skip to main content
15,883,841 members
Articles / Web Development / ASP.NET

A Smart Form Control for ASP.NET URL Rewriting

Rate me:
Please Sign up or sign in to vote.
4.20/5 (8 votes)
20 Jul 2007CPOL3 min read 58.7K   457   39   10
A smart form control that fixes many of the problems that are caused by extensionless URL rewriting in ASP.NET.

Introduction

One of the websites I work on uses URL rewriting, i.e., when you type in an address www.somesite.com/Products/WidgetA/, you are really browsing to an ASP.NET page somewhere else in the website, say: www.somesite.com/ProductDisplay.aspx?page=Products/WidgetA/.

For a full explanation of URL rewriting along with a very usable rewriting engine, go to 15seconds.com and read Rewrite.NET -- A URL Rewriting Engine for .NET, by Robert Chartier.

Background

One problem with this, and believe me, there are many problems with URL rewriting, is that it breaks the form tag. The action of the form tag will be the real address of the .aspx page and not the nice friendly address you wanted. To workaround this problem, MSDN suggests that you create an "Action-less form", meaning, you create a custom form tag that simply doesn't write the action attribute on the form tag.

C#
namespace ActionlessForm {
  public class Form : System.Web.UI.HtmlControls.HtmlForm
  {
     protected override void RenderAttributes(HtmlTextWriter writer)
     {
        writer.WriteAttribute("name", this.Name);
        base.Attributes.Remove("name");

        writer.WriteAttribute("method", this.Method);
        base.Attributes.Remove("method");

        this.Attributes.Render(writer);

        base.Attributes.Remove("action");

        if (base.ID != null)
           writer.WriteAttribute("id", base.ClientID);
     }
  }
}
//From <a href="%22http://msdn2.microsoft.com/en-us/library/ms972974.aspx%22">URL Rewriting in ASP.NET</a> by Scott Mitchell

The big problem with the above code is that it completely breaks the form tag for any server control that hooks into the postback cycle, validators for instance. If you look at the source code for the RenderAttributes method the HtmlForm class in the .NET Framework using the excellent Lutz Reflector, you will see that there is a whole lot going on in the .NET Framework's HtmlForm class that Scott's version does not duplicate. Besides that, what if the HtmlForm tag was changed in a future version of the .NET Framework?

If we examine exactly how the action attribute gets written, we can see that because the GetActionAttribute is private, there is no way to directly change the action attribute value before it gets to the HtmlTextWriter.

Also, some of the logic in the RenderAttributes method simply can't be reproduced using a derived class because many of the necessary methods and attributes are private.

C#
writer.WriteAttribute("action", this.GetActionAttribute(), true);

So, we need to take a different approach to take control over if / how the Form tag action attribute gets written.

Using the code

My approach was to intercept the attribute before it was written to the HtmlTextWriter. To do this, I created a derived class called SelectiveHtmlTextWriter and overrode the WriteAttribute method:

C#
public class SelectiveHtmlTextWriter : HtmlTextWriter
{
    ...
    
    public override void WriteAttribute(string name, string value, bool fEncode) 
    { 
        base.WriteAttribute(name, "/newpagename/goes/here/", fEncode);
    }
}


public class SmartForm : HtmlForm
{
    ...

    protected override void RenderAttributes(HtmlTextWriter writer)
    {
        SelectiveHtmlTextWriter customWriter = new SelectiveHtmlTextWriter(writer);
        base.RenderAttributes(customWriter);
    }
}

A new SelectiveHtmlTextWriter object is created and passed into the HtmlForm class RenderAttribute method in place of the normal HtmlTextWriter.

This works because of polymorphism. All the HtmlForm class cares about is that it gets a reference to an HtmlTextWriter. However, when it calls RenderAttribute, my override gets called instead.

This works great, but I have never been fond of hard-coded values, so in order to make this more extensible, I added an event to the new SelectiveHtmlTextWriter called WritingAttribute which the SmartForm class subscribes to. This allows any attribute, specifically action, to be modified before its value is written. In this case, I am substituting the Request.RawUrl value which holds the actual URL that the request came from.

C#
public class SmartForm : HtmlForm
{
    public SmartForm()
        : base()
    {
    }

    protected override void RenderAttributes(HtmlTextWriter writer)
    {
        SelectiveHtmlTextWriter customWriter = new SelectiveHtmlTextWriter(writer);
        customWriter.WritingAttribute += 
          new SubstituteValueEventHandler(customWriter_WritingAttribute);
        base.RenderAttributes(customWriter);
    }

    void customWriter_WritingAttribute(object sender, SubstituteValueEventArgs e)
    {
        if (e.Name == "action")
        {
            e.NewValue = Context.Request.RawUrl;
        }
    }
}

In conclusion, if I had a choice, I would not use URL rewriting at all. I would find some other way to make the client happy. But, the SmartForm that we outlined here will ease the pain quite a bit. One last caveat, using the SmartForm breaks the Visual Studio designer. I have been unsuccessful at creating a custom designer for this. If anybody comes up with a designer for this, email me and I'll add it to the article with the appropriate credits.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
I have been doing web development for about 11 years. I specialize in Dynamic HTML, C#, and SQL database design.

Comments and Discussions

 
GeneralMy vote of 1 Pin
Rakesh S S24-May-12 19:59
Rakesh S S24-May-12 19:59 
GeneralAn easier option is here Pin
robert_fairbrother27-Aug-07 6:45
robert_fairbrother27-Aug-07 6:45 
GeneralRe: An easier option is here Pin
Rakesh S S24-May-12 19:57
Rakesh S S24-May-12 19:57 
GeneralWeb.Config Pin
Xela22023-Jul-07 15:41
Xela22023-Jul-07 15:41 
GeneralClasses derived from HtmlForm break the designer Pin
nikkilong23-Jul-07 7:08
nikkilong23-Jul-07 7:08 
GeneralRe: Classes derived from HtmlForm break the designer Pin
Richard Deeming27-Jul-07 10:04
mveRichard Deeming27-Jul-07 10:04 
GeneralActually the best workaround would be.... Pin
Gevorg23-Jul-07 5:05
Gevorg23-Jul-07 5:05 
QuestionReverse engineering? Pin
Dario Solera21-Jul-07 1:42
Dario Solera21-Jul-07 1:42 
AnswerRe: Reverse engineering? Pin
David T Nelson23-Jul-07 7:05
David T Nelson23-Jul-07 7:05 
AnswerRe: Reverse engineering? Pin
nsimeonov24-Jul-07 1:26
nsimeonov24-Jul-07 1:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.