在進行了URL Rewrite之後,經常會遇到的問題就是頁面中PostBack的目標地址並非客戶端請求的地址,而是URL Rewrite之後的地址。以上一篇文章中的重寫為例:
<rewriter> <rewrite url="^/User/(\d+)$" to="~/User.aspx?id=$1" processing="stop" /> <rewrite url="^/User/(\w+)$" to="~/User.aspx?name=$1" processing="stop" /> </rewriter>
當用戶請求“/User/jeffz”之後,頁面中的出現的代碼卻會是<form action="/User.aspx?name=jeffz" />,這是因為在生成代碼時,頁面會使用當前Request.Url.PathAndQuery的值來得到form元素的action。這導致了一旦PostBack,地址欄裡就會出現“User.aspx?name=jeffz”,而這個地址很可能是請求不到正確的資源的(因為可能被Rewrite到了別處,或者由於目錄級別的關系而根本沒有該資源)。在之前《UpdatePanel與UrlRewrite》一文中,我說可以在頁面末尾添加一行JavaScript代碼來解決這個問題:
<script language="javascript" type="text/javascript"> document.getElementsByTagName("form")[0].action = window.location; </script>
這行代碼的意圖非常明顯,將form的action修改為window.location(即浏覽器地址欄中的路徑),這樣當頁面進行PostBack時,目標地址就會是URL Rewrite之前的地址了。這種做法能夠讓程序正常運行,但是實在不能讓我滿意。為什麼?
因為太丑了。
因為我們還是把URL Rewrite之後的地址暴露給了客戶端。用戶只要裝一個HTTP嗅探器(例如著名的Fiddler),或者在IE中直接選擇查看源文件,我們的目標地址就毫無遮掩的顯示在用戶面前了。怎麼能讓用戶知道我們的重寫規則?我們必須解決這個問題。解決的方法很簡單,也已經非常流行了,那就是使用Control Adaptor來改變Form生成時的行為。不過讓我感到比較奇怪的是,關於這個Control Adaptor,在網絡上搜到的盡是VB.NET的版本,倒是微軟主推的C#語言卻找不到。雖然只要了解一點VB.NET的語法要改寫起來並不困難,但是畢竟也是個額外的工作啊。所以我現在就將這個Adaptor的C#版本代碼貼出來,以便朋友們能夠直接使用:
namespace Sample.Web.UI.Adapters { public class FormRewriterControlAdapter : System.Web.UI.Adapters.ControlAdapter { protected override void Render(HtmlTextWriter writer) { base.Render(new RewriteFormHtmlTextWriter(writer)); } } public class RewriteFormHtmlTextWriter : HtmlTextWriter { public RewriteFormHtmlTextWriter(HtmlTextWriter writer) : base(writer) { this.InnerWriter = writer.InnerWriter; } public RewriteFormHtmlTextWriter(TextWriter writer) : base(writer) { this.InnerWriter = writer; } public override void WriteAttribute(string name, string value, bool fEncode) { if (name == "action") { HttpContext context = HttpContext.Current; if (context.Items["ActionAlreadyWritten"] == null) { value = context.Request.RawUrl; context.Items["ActionAlreadyWritten"] = true; } } base.WriteAttribute(name, value, fEncode); } } }