天天躁日日躁狠狠躁AV麻豆-天天躁人人躁人人躁狂躁-天天澡夜夜澡人人澡-天天影视香色欲综合网-国产成人女人在线视频观看-国产成人女人视频在线观看

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

問題遠沒結(jié)束

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

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

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

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

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

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

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

  我們?nèi)绻刑貏e的需求,就可以通過定義一個FilterForbiddenWordHandlerAttribute的子類,重載GetFilterType方法,然后標(biāo)記在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{    ...}

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

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標(biāo)記信息,所以這次更新的關(guān)鍵是我們必須推遲進行過濾的時機。推遲到哪個階段?自然是能夠確定HttpHandler類型的最早時機,PostMapRequestHandler。我們通過反射來獲取Handler類型上的FilterForbiddenWordAttribute子類的信息,作為Filter傳入帶有額外參數(shù)的ProcessCollection方法中。ProcessCollection方法內(nèi)部會調(diào)用根據(jù)filter參數(shù)來確定某個key的過濾方式:正常(當(dāng)作純文本進行過濾)、忽略(不過濾)、JSON(只過濾JSON內(nèi)元素的值)以及HTML(忽視tag和attribute,并考慮文字內(nèi)的HTML Encode)。其余不變。

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

繼續(xù)改進

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

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

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

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

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

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

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

  如果在某些情況中,我們也可以把對象本身作為參數(shù)傳入Custom Attribute的方法中,Attribute方法內(nèi)部根據(jù)參數(shù)的屬性來實現(xiàn)邏輯,可惜的是,Page類內(nèi)部的控件成員是protected變量,無法從外部訪問。對于我們來說,使Http Handler(即頁面)直接實現(xiàn)某個接口的最大(唯一?)好處,就是讓該接口的成員可以訪問頁面內(nèi)部的非公開成員了。這點就是問題關(guān)鍵,我們現(xiàn)在不必直接使用txtPassword這個常量,而是能夠訪問頁面中的txtPassword控件來獲取它相關(guān)的屬性(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上的修改,相信不會難道您,老趙就不在這里說帖太多代碼浪費帶寬了??梢钥闯?,現(xiàn)在的代碼中已經(jīng)沒有了txtPassword這個常量,取而代之的是對txtPassword對象ID的訪問。現(xiàn)在如果在ASPx中修改了這個控件的ID,那么在ASPx.cs中的變量也會被重構(gòu)至對應(yīng)名字,這大大提高了開發(fā)效率,降低了出錯可能。

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

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

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

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

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

 

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

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

主站蜘蛛池模板: 色欲AV精品人妻一区二区麻豆 | 欧美GV肉片视频免费观看 | 92看看福利午夜影院 | 年轻的朋友4在线看中文字幕 | 国产在线精品视亚洲不卡 | 男女久久久国产一区二区三区 | 女厕所边摸边吃奶边做爽视频 | 精品国产精品人妻久久无码五月天 | 久久综合伊人 | 国产午夜精品AV一区二区麻豆 | 99久久精品免费国产一区二区三区 | 亚洲精品中文字幕制 | 快播h动漫网 | 快穿女主有名器的H纯肉黄暴拉文 | 三级黄色在线看 | 538prom精品视频我们不只是 | 国产在线精品视亚洲不卡 | 蜜桃99影院| 无码不卡中文字幕在线观看 | 久久天天躁狠狠躁夜夜呲 | 无套内射在线观看THEPORN | 欧美内射深插日本少妇 | 国语对白刺激真实精品 | 99er4久久视频精品首页 | 久草在线福利视频在线播放 | 天美麻豆成人AV精品 | 国产精品悠悠久久人妻精品 | 国产在线精品亚洲另类 | 啊灬啊灬啊灬快灬深高潮啦 | 国产亚洲精品久久久999密臂 | 洲精品无码高潮喷水A片 | 色欲国产麻豆精品AV免费 | 国产偷窥盗摄一区二区 | 亚洲日韩中文字幕日本有码 | a视频免费看 | 岳的奶大又白又胖 | XXX国产麻豆HD真实乱 | 男人插女人逼逼 | 色婷婷激婷婷深爱五月小蛇 | 怡红院美国分院一区二区 | 亚洲欧美韩国综合色 |