TIP: How to Handle Form Postbacks when Url Rewriting






4.50/5 (2 votes)
Url Rewriting is great and I love it a lot, you get to create nice readable (and memorable) urls without having to create hundred of files. However, there can be issues.
Url Rewriting is great and I love it a lot, you get to create nice readable (and memorable) urls without having to create hundred of files. However, there can be issues.
For example, when you post back to a page where the url has been rewritten, it won’t be to the nice alias, it will be to the direct page.aspx?id=blah location. Which is a bit nasty.
To stop this, I make use of a Control Adapters which alters the rendering logic of the HtmlForm control on every ASP.Net page, to amend the action to be the nice alias.
1: using System; 2: using System.Collections.Generic; 3: using System.Linq; 4: using System.Text; 5: using System.Web.UI.HtmlControls; 6: using System.Web; 7: 8: namespace MartinOnDotNet.Web.ControlAdapters 9: { 10: /// <summary> 11: /// Control adapter to ensure the form action persists the rewritten url 12: /// </summary> 13: public class FormActionRewriterControlAdapter : System.Web.UI.Adapters.ControlAdapter 14: { 15: /// <summary> 16: /// Overrides the <see cref="M:System.Web.UI.Control.OnPreRender(System.EventArgs)"/> method for the associated control. 17: /// </summary> 18: /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param> 19: protected override void OnPreRender(EventArgs e) 20: { 21: HtmlForm form = Control as HtmlForm; 22: if (form != null && HttpContext.Current != null) 23: { 24: form.Action = HttpContext.Current.Request.RawUrl; 25: } 26: base.OnPreRender(e); 27: } 28: 29: /// <summary> 30: /// Generates the target-specific markup for the control to which the control adapter is attached. 31: /// </summary> 32: /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> to use to render the target-specific output.</param> 33: protected override void Render(System.Web.UI.HtmlTextWriter writer) 34: { 35: 36: base.Render(new RewriteFormActionHtmlTextWriter(writer)); 37: } 38: } 39: }
You'll notice that the control adapter needs a special HtmlTextWriter implementation to do the actual heavy lifting:
1: using System; 2: using System.Collections.Generic; 3: using System.Linq; 4: using System.Text; 5: using System.Web; 6: using System.Web.UI; 7: using System.IO; 8: 9: namespace MartinOnDotNet.Web.ControlExtensions 10: { 11: /// <summary> 12: /// Specialised <see ref="HtmlTextWriter" /> that handles populating the form action attribute appropriately for 13: /// url rewriting 14: /// </summary> 15: public class RewriteFormActionHtmlTextWriter : HtmlTextWriter 16: { 17: private bool _haveAlreadyWritten; 18: 19: /// <summary> 20: /// Initializes a new instance of the <see cref="RewriteFormActionHtmlTextWriter"/> class. 21: /// </summary> 22: /// <param name="writer">The writer.</param> 23: public RewriteFormActionHtmlTextWriter(TextWriter writer) : base(writer) { InnerWriter = writer; } 24: 25: /// <summary> 26: /// Initializes a new instance of the <see cref="RewriteFormActionHtmlTextWriter"/> class. 27: /// </summary> 28: /// <param name="writer">The writer.</param> 29: public RewriteFormActionHtmlTextWriter(HtmlTextWriter writer) : base(writer) { InnerWriter = writer.InnerWriter; } 30: 31: /// <summary> 32: /// Writes the specified markup attribute and value to the output stream, and, if specified, writes the value encoded. 33: /// </summary> 34: /// <param name="name">The markup attribute to write to the output stream.</param> 35: /// <param name="value">The value assigned to the attribute.</param> 36: /// <param name="fEncode">true to encode the attribute and its assigned value; otherwise, false.</param> 37: public override void WriteAttribute(string name, string value, bool fEncode) 38: { 39: if (string.Equals(name, "action", StringComparison.OrdinalIgnoreCase) && !_haveAlreadyWritten) 40: { 41: 42: value = HttpContext.Current.Request.RawUrl; 43: _haveAlreadyWritten = true; 44: 45: } 46: base.WriteAttribute(name, value, fEncode); 47: } 48: } 49: }
This is then all registered in your web application with a simple FormAdapter.Browser file in the App_Browsers folder of your Web App.
1: <browsers> 2: <browser refID="Default"> 3: <controlAdapters> 4: <adapter controlType="System.Web.UI.HtmlControls.HtmlForm" 5: adapterType="MartinOnDotNet.Web.ControlAdapters.FormActionRewriterControlAdapter" /> 6: 7: </controlAdapters> 8: </browser> 9: </browsers>
These clases/config can now be reused across multiple projects quickly and easily.
Enjoy.