65.9K
CodeProject is changing. Read more.
Home

TIP: How to Handle Form Postbacks when Url Rewriting

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (2 votes)

Jul 15, 2010

CPOL
viewsIcon

13801

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.