5,664,339 members and growing! (15,475 online)
Email Password   helpLost your password?
Web Development » ASP.NET » General     Intermediate

A Smart Form Control for ASP.NET URL Rewriting

By David T Nelson

A smart form control that fixes many of the problem that are caused by extensionless url rewriting in ASP.NET
C# 2.0, C#, Windows, .NET, .NET 3.0, .NET 2.0, WebForms, ASP.NET, VS2005, Visual Studio, Dev

Posted: 20 Jul 2007
Updated: 20 Jul 2007
Views: 12,674
Bookmarked: 23 times
Announcements
Loading...



Search    
Advanced Search
Sitemap
5 votes for this Article.
Popularity: 3.03 Rating: 4.33 out of 5
0 votes, 0.0%
1
0 votes, 0.0%
2
1 vote, 20.0%
3
1 vote, 20.0%
4
3 votes, 60.0%
5
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report This Article

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 work around 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.

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 "http://msdn2.microsoft.com/en-us/library/ms972974.aspx">URL Rewriting in ASP.NET 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 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 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.

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:
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 HtmlForm class RenderAttribute method in place of the normal the HtmlTextWriter.

This works because of polymorphism. All the HtmlForm class is cares about is that it gets a reference to an HtmlTextWriter. However when it calls the 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, but specifically the 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.

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

David T Nelson


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

Other popular ASP.NET articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 8 of 8 (Total in Forum: 8) (Refresh)FirstPrevNext
GeneralAn easier option is herememberrobert_fairbrother7:45 27 Aug '07  
GeneralWeb.ConfigmemberXela22016:41 23 Jul '07  
GeneralClasses derived from HtmlForm break the designermembernikkilong8:08 23 Jul '07  
GeneralRe: Classes derived from HtmlForm break the designermemberRichard Deeming11:04 27 Jul '07  
GeneralActually the best workaround would be....memberGevorg6:05 23 Jul '07  
GeneralReverse engineering?memberDario Solera2:42 21 Jul '07  
GeneralRe: Reverse engineering?memberDavid T Nelson8:05 23 Jul '07  
GeneralRe: Reverse engineering?membernsimeonov2:26 24 Jul '07  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 20 Jul 2007
Editor:
Copyright 2007 by David T Nelson
Everything else Copyright © CodeProject, 1999-2008
Web07 | Advertise on the Code Project