一区二区久久-一区二区三区www-一区二区三区久久-一区二区三区久久精品-麻豆国产一区二区在线观看-麻豆国产视频

一個較完整的關鍵字過濾解決方案(中)

問題遠沒結束

  上面的問題解決了沒有?哦哦,我是指采取命名約定的方式來改變過濾行為。當然有問題,不過我這里提一下比較重要的兩個:

  首先,就是“改名”這種行為——究竟是否方便?還記得我們的需求嗎(提示一下:方便、通用……)?如果采取上面的命名約定方案,我們可能就需要在頁面的前端和后端都不斷地改名,一會兒加-noffw,一會兒加-json。如果項目只由您來負責這還好辦,只是麻煩一些,但是如果您的團隊中的前臺開發人員性格古怪,固執己見,不愿配合怎么辦(打架我喜歡,可惜不能直接解決問題)?再者,假如您除了一個FilterForbiddenWordModule之外還有類似的“FilterScriptInjectionModule”怎么辦(別真寫這么一個HttpModule,不合適,老趙只是想不出一個恰當的例子了)?如果兩個Module都采取命名約定的方式,那么如何制定一個兩者能同時認同的約定就也是個麻煩事。

  再者,命名真是我們可以控制的嗎?某些情況下好說,但是假如您在使用WebForms中的控件怎么辦?WebForm中的一個重要特性就是用過Naming Container來避免客戶端ID的沖突。假設我們的頁面是放在一個Master Page中ID為Main的ContentPlaceHolder中,那么ID為txtPassword的文本框在客戶端里生成的HTML便會如下所示——那么我們又能有什么辦法可以做到“命名約定”嗎?

<input name="ctl00$Main$txtPassword" id="ctl00_Main_txtPassword">input>

  嘿,看來這種命名約定的方式有時候真不是那么通用啊。那么我就來設法解決WebForm這個問題。

  其實如果要解決WebForm這個問題,說白了就是要設法可以讓服務器端明確指定一些字段的處理方式。這種“特殊”則意味著對于過濾方式的判斷必須與特定的Page——泛化一下,HttpHandler進行綁定。這里我先談一下我的第一個想法:使用Custom Attribute進行標記的方式。我們構造一個FilterForbiddenWordAttribute,其中包含一個抽象GetFilterType方法根據key來指定過濾方式:

public enum FilterForbiddenWordType{    Ignored,    Normal,    Json,    Html}public abstract class FilterForbiddenWordAttribute : Attribute{    public abstract FilterForbiddenWordType GetFilterType(string key);}

  我們如果有特別的需求,就可以通過定義一個FilterForbiddenWordHandlerAttribute的子類,重載GetFilterType方法,然后標記在HttpHandler上。如下:

public class DefaultFilterForbiddenWordAttribute :    FilterForbiddenWordAttribute{    public override FilterForbiddenWordType GetFilterType(string key)    {        if (key.EndsWith("txtPassword"))        {            return FilterForbiddenWordType.Ignored;        }        return FilterForbiddenWordType.Normal;    }}[DefaultFilterForbiddenWord]public partial class Default : System.Web.UI.Page{    ...}

  當然,我們還需要對FilterForbiddenWordModule進行一些修改才能使之生效(朋友們可以先不要看代碼,想想這次改變的關鍵在哪里?):

public class FilterForbiddenWordModule : IHttpModule{    ...    void IHttpModule.Init(HttpApplication context)    {        context.PostMapRequestHandler += new EventHandler(OnPostMapRequestHandler);    }    private static void OnPostMapRequestHandler(object sender, EventArgs e)    {        var context = (sender as HttpApplication).Context;        var handlerType = context.Handler.GetType();        var filter = ((FilterForbiddenWordAttribute[])handlerType.GetCustomAttributes(            typeof(FilterForbiddenWordAttribute), true)).FirstOrDefault();         ProcessCollection(context.Request.QueryString, filter);        ProcessCollection(context.Request.Form, filter);    }    private static void ProcessCollection(        NameValueCollection collection,        FilterForbiddenWordAttribute filter)    {        var copy = new NameValueCollection();        foreach (string key in collection.AllKeys)        {            var filterType = (filter == null) ? FilterForbiddenWordType.Normal                : filter.GetFilterType(key);            Array.ForEach(                collection.GetValues(key),                v => copy.Add(key, ForbiddenWord.Filter(v, filterType)));        }        ...    }}

  修改示例。例如我們在頁面上放置兩個文本框txtPassword和txtNormal:

<ASP:TextBox ID="txtPassword" runat="server" TextMode="MultiLine" /><ASP:TextBox ID="txtNormal" runat="server" TextMode="MultiLine" /><ASP:Button ID="Button1" runat="server" Text="Click" />

  點擊,效果不言而喻:

  公布答案:因為我們需要等到確認了HttpHandler類型才能獲得FilterForbiddenWordAttribute標記信息,所以這次更新的關鍵是我們必須推遲進行過濾的時機。推遲到哪個階段?自然是能夠確定HttpHandler類型的最早時機,PostMapRequestHandler。我們通過反射來獲取Handler類型上的FilterForbiddenWordAttribute子類的信息,作為Filter傳入帶有額外參數的ProcessCollection方法中。ProcessCollection方法內部會調用根據filter參數來確定某個key的過濾方式:正常(當作純文本進行過濾)、忽略(不過濾)、JSON(只過濾JSON內元素的值)以及HTML(忽視tag和attribute,并考慮文字內的HTML Encode)。其余不變。

  順便說一句,以上代碼其實只是為了寫這些內容而在10分鐘內寫好的,不考慮性能、緩存、同步、邊界等情況——因為我相信看了下面的文字您一定會拋棄這種做法。

繼續改進

  上面的做法(相對使用命名約定的方式)改進了什么地方?很簡單,之前提到的命名約定的缺點就是上述做法的優點:

  1. 不同Page(Http Handler)可以自行指定字段所需要的過濾邏輯。
  2. 無需前端改名,只需后端標記。
  3. 避免復雜的命名約定,使多種橫切型的過濾功能可以輕易共存。

  真是美妙地嗷嗷的,但是有沒有朋友看出問題來?我提示一下:GetFilterType方法中使用了一個常量字符串txtPassword。

  估計有朋友會問:“咦,這有什么問題?”粗看似乎沒有,不過老趙看到代碼中出現常量總是要警惕一番(自覺是個好喜歡):為啥要是txtPassword而不是txtPassWord(一個常見的拼寫錯誤)?為啥代碼中用0而不用-1?這里的問題倒不是說一個常量在代碼中到處使用時最好使用一個const——不不,是readonly字段來代替(為啥用const不太好?)。而是……再提示一下,如果某人將頁面上的txtName文本框改為txtUserName那會出現何種情況?

  嗯嗯,那么Attribute中的GetFilterType方法當然還是在判斷一個key是否由txtName結尾,而我們修改后的頁面中Post內容中已經變成了txtUserName,咋整?但是可悲的是,我們尊敬的Attribute,就算你拿刀威脅它它也沒法知道該替換什么啊。唉,那又有誰才能知道呢?不用多想,當然是頁面本身了。

  .NET中Custom Attribute的特性深入人心,大大增強了.NET中反射機制的可用性,也因此Kent Beck認為NUnit的設計和使用較JUnit更為優雅。老趙的項目中也到處可見Custom Attribute的存在,寫出的代碼也簡單優美強大地很。不過用多了Custom Attribute也造成了一種思維定勢,一些“附加功能”往往都喜歡往上靠,很多問題往往一個功能出來三秒不到腦子里就浮出一個利用Custom Attribute的解決方案。古語有云,“世界如此美好,我卻如此浮躁,這樣不好,不好……”。事實上ASP.NET框架中已經有了不使用Custom Attribute進行“標記”的現成示例。例如,您知道IRequiresSessionState接口和INamingContainer接口的作用嗎?

  如果您翻過IRequriedSessionState和INamingContainer接口的文檔,就會發現它們有個共同的特點——沒有任何成員。這意味著什么呢?這意味著實現了這樣的接口的類,唯一的作用就是“別人知道你實現了這個接口”。有點拗口,對吧?其實就是指,這兩個接口只起到了標記的作用。使用Custom Attribute或使用接口對一個類進行標記和擴展的優劣取舍,我打算用額外的一篇文章來討論這個問題(要不現在大家來Brain Storm一下如何?)。目前,朋友們只需關心一點,如果不用Custom Attirubte而使用接口,我們該如何改寫上面的程序。并且,這種改變帶來了什么好處?

  如果在某些情況中,我們也可以把對象本身作為參數傳入Custom Attribute的方法中,Attribute方法內部根據參數的屬性來實現邏輯,可惜的是,Page類內部的控件成員是protected變量,無法從外部訪問。對于我們來說,使Http Handler(即頁面)直接實現某個接口的最大(唯一?)好處,就是讓該接口的成員可以訪問頁面內部的非公開成員了。這點就是問題關鍵,我們現在不必直接使用txtPassword這個常量,而是能夠訪問頁面中的txtPassword控件來獲取它相關的屬性(ID)。不再贅述,修改如下:

public interface IForbiddenWordFilter{    FilterForbiddenWordType GetFilterType(string key);}public partial class Default : System.Web.UI.Page, IForbiddenWordFilter{    ...    FilterForbiddenWordType IForbiddenWordFilter.GetFilterType(string key)    {        if (key.EndsWith(this.txtPassword.ID)) return FilterForbiddenWordType.Ignored;        return FilterForbiddenWordType.Normal;    }}

  至于HttpModule上的修改,相信不會難道您,老趙就不在這里說帖太多代碼浪費帶寬了。可以看出,現在的代碼中已經沒有了txtPassword這個常量,取而代之的是對txtPassword對象ID的訪問。現在如果在ASPx中修改了這個控件的ID,那么在ASPx.cs中的變量也會被重構至對應名字,這大大提高了開發效率,降低了出錯可能。

  差點忘說了一句,大家千萬不要忘了對于WebForms模型,有幾個特定的key是不能替換的例如“__VIEWSTATE”和“__VIEWSTATEENCRYPTED”。關于這點,老趙的作法是忽略所有以兩條下劃線作為開頭的Key以保護WebForms模型內部需求。

  結合上一篇文章《一個較完整的關鍵字過濾解決方案(上)》,這似乎就是個較為完整的解決方案,不過這個話題結束了嗎?當然沒有。在下一篇文章《一個較完整的關鍵字過濾解決方案(下)》里,我們將討論幾個額外的話題,例如:

  • 這個解決方案的適用場合?不適用場合?
  • 輸入過濾?輸出過濾?
  • 我們一定要使用HttpModule進行過濾嗎?
  • 性能?

  此外,我想大家在看了這篇文章后來一起思考一些問題,而我對于這些問題的看法也會在下一篇文章中談到:

  • 在WebForms模型中,Page即是一個Handler,于是可以實現IForbiddenWordFilter。那么Page里Control所需要過濾的內容呢?動態加載的Control呢?
  • 這篇文章的示例里有個陷阱,您看的出是在哪里嗎?

 

NET技術一個較完整的關鍵字過濾解決方案(中),轉載需保留來源!

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

主站蜘蛛池模板: 牛牛本精品99久久精品88m | 美女很黄很黄是免费的·无遮挡网站 | 免费四虎永久在线精品 | 国语自产自拍秒拍在线视频 | 亚洲成a人片77777kkkk | 亚洲最大综合网 | 色噜噜狠狠在爱丁香 | 美女网站一区二区三区 | haodiaocao这里只有精品一 | 久草小区二区三区四区网页 | 九九热久久免费视频 | 日韩一区二区免费看 | 涩涩涩爱| 韩国一级毛片a级免观看 | 国产亚洲综合成人91精品 | 久久精品国产无限资源 | 中文字幕精品一区二区三区视频 | 4hu四虎永久网址 | www.五月婷婷| 小说区乱图片区 | 青草悠悠视频在线观看 | 91啦视频在线| 色哟哟在线观看精品大全视频 | 日韩国产一区二区 | 香蕉av影院 | 国产精品久久久久免费视频 | 黄色视屏网站 | 91精品小视频 | 51国产午夜精品免费视频 | 亚洲精品综合在线 | 欧美一级精品 | 福利国产精品 | 四虎影视在线永久免费看黄 | 久久有码中文字幕 | 麻豆视频导航 | 激情网站视频 | 国产精品视频一区二区三区w | 啪啪色网| 国产精品福利在线观看入口 | 中文激情 | 噜噜噜 综合 亚洲 |