Tuesday, October 12, 2004

Adventures In Url Rewriting

A project I'm currently working on for my employer using ASP.NET 1.1 requires a coherent mechanism to maintain a consistent look and feel throughout the entire site. Now, as ASP.NET 2.0 isn't here yet (Master Pages would have been a perfect fit for this project) I decided to use a mechanism based on URL rewriting.

In short, all the main 'page' functionality is contained within User Controls. There is a single .aspx page that loads the required user control and instantiates it for display within the HTML template. Using a regular expression based url rewriter mechanism this templating system is hidden from the end user; for example, a request for:

http://localhost/MyApplication/MyPage.aspx

is mapped under the hood to:

http://localhost/MyApplication/Template.aspx?UserControl=Controls/MyPage.ascx

All well and good - I had the basic URL rewriting mechanism in place in a couple of hours, and the start of a working template mechanism a couple of hours after that. However, then the problems started cropping up.

The first major issue cropped up with postbacks. In short, they didn't work. The reason for this is that the ASP.NET framework fills in the action attribute with a canonicalized path to CurrentExecutionFilePath, which is not what's wanted here.

Easy then - change the action in the template page. Wrong; whichever brain dead idiot designed the System.Web.UI.HtmlControls.HtmlForm class hid the implementation of the action attribute. So, you've got to jump through hoops to change the functionality.

My eventual solution was to subclass the HtmlForm and replace the instance in my template page with my new class, ActionForm. ActionForm overrides the RenderAttributes function, which creates a new HtmlTextWriter, my own subclassed ActionFormHtmlTextWriter. This class in turn overrides WriteAttribute which checks to see what attribute it's writing out - if it's "action", then the value is changed to the originally requested Url. Control is then passed back to the original HtmlTextWriter to write the value out. Anyway, if it gets someone else out of the mire, then great.

public class ActionForm : HtmlForm
{
    protected override void RenderAttributes(HtmlTextWriter writer)
    {
        string action = (string)Context.Items["VirtualUrl"];

        if (action == null)
        base.RenderAttributes(writer);

        using (ActionFormHtmlTextWriter virtualWriter = new ActionFormHtmlTextWriter(writer))
        {
            virtualWriter.ActionUrl = action;
            base.RenderAttributes(virtualWriter);
        }
    }

    private class ActionFormHtmlTextWriter : HtmlTextWriter
    {
        private string actionUrl;

        public ActionFormHtmlTextWriter(HtmlTextWriter writer) : base(writer)
        {
        }

        public ActionFormHtmlTextWriter(HtmlTextWriter writer, string tabString) : base(writer, tabString)
        {
        }

        public string ActionUrl
        {
            get { return actionUrl; }
            set { actionUrl = value; }
        }

        public override void WriteAttribute(string name, string value, bool fEncode)
        {
            if (value != null && String.Compare(name, "action", true) == 0)
                value = ActionUrl;

            HtmlTextWriter writer = (HtmlTextWriter)InnerWriter;
            writer.WriteAttribute(name, value, fEncode);
        }
    }
}


 

Wednesday, December 29, 2004 2:24:50 PM UTC
You, just solved my problem!
I started with URL rewriting today and had this problem with the form as well.

Thank you very much for you soultion.
Tuesday, March 29, 2005 3:32:53 PM UTC
You made my headache disappear. Apparently the .NET developers didn't want us to manually change the action. A simple rewrite of RenderAttributes was enough.
Steven
Thursday, April 14, 2005 12:24:05 PM UTC
Many Thanks, i spent 1 day and finally remembered google, it help me to reach you, just copy and paste, that's amazing.
Tuesday, May 10, 2005 1:28:18 PM UTC
I am not getting how you have this working, do you have a small sample project that people can download to play with?
Sharky
Sunday, March 12, 2006 12:33:36 PM UTC
Oh, I also met this problem, you've provieded a good solution. So many thanks..
Tuesday, December 11, 2007 9:01:59 PM UTC
Hi, mate.
The other thing is that if you overwrite it, the visual designer cannot display it properly. The subclass is just a square on the screen.
The solutions that I found some time ago is:

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);
}
Rafal
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview