ASP.NET設計控件凈化網站語言
一、概述
考慮一下這種情形:你為一個Web網站寫了一個應用程序,它的功能是接受用戶的輸入并將輸入內容永久保存,例如保存到數據庫,另外還要在網站上顯示出用戶輸入的內容,例如論壇就是一個很典型的例子。
如果用戶來源很雜,必須考慮如何防止用戶提交和張貼攻擊性(或者色情的、庸俗的)的內容。可能的解決方案包括:
⑴ 將用戶群限制到一個封閉的用戶團體,即要求用戶使用程序功能之前必須先注冊/登錄。這樣,由于每次提交的內容都可以追查到提交者,用戶破壞網站規則的可能性就小了很多。如果有用戶做出了不應該做的事,你就可以核實用戶身份,予以相應的處理。
⑵ 在網站上發布用戶提交的內容之前,先由管理員審閱。很多時候,由于人力資源有限,這個辦法不一定行得通。
⑶ 禁止用戶提交攻擊性內容。這是最理想的解決辦法,把問題解決在起源。但具體應該怎么實現呢?
本文介紹的方案以一個復合控件為基礎,利用一個XML文件來定義攻擊性詞語。我們將用VB.NET編寫這個復合控件,用普通的文本編輯器和命令行編譯器(vbc)完成整個工程的構建。
在正式編寫控件之前,首先我們來簡單地回顧一下ASP.NET中控件的概念。本文出現的所有控件都是服務器控件,它們在服務器上運行,將HTML代碼發送到客戶端。要理解控件的分類,可以從控件是否嵌入到Web表單頁面(因而采用按需編譯方式)或預先編譯的角度來觀察。微軟定義了下列ASP.NET服務器控件:HTML服務器控件,Web服務器控件,驗證控件,用戶控件。
前三種控件讀者應該已經比較熟悉了,對于開發者來說,它們是最簡單的控件類型,在ASP.NET中已經由微軟為我們編寫好。用戶控件則有所不同。用戶控件是“包裝”成.ascx頁面形式的.aspx頁面,其他.aspx頁面可以通過注冊和實例化來調用用戶控件的功能。這是一種被寄予厚望的服務器端控件,對于ASP/ASP.NET開發者來說,它代表著一大進步,特別地,現在編寫控件的語言已經全面支持面向對象技術。
ASP.NET用戶控件由一個或多個服務器控件、靜態HTML元素構成,可以包含額外的代碼,每個用戶控件封裝一組特定的功能。用戶控件可以通過簡單地擴展現有服務器控件(控件組)得到,例如,帶有旋轉功能的圖形控件,在文本框中保存日期的日歷控件。
二、開發復合控件
控件要檢查用戶提交的內容是否包含“攻擊性”詞語,攻擊性詞語由一個XML文件定義,XML文件的結構如下:
<?xml version="1.0"? encoding="GB2312"> <words> <word>詞語一</word> <word>詞語二</word> </words> |
本文的復合控件(Composite)包含三個ASP.NET服務器控件:一個Textbox控件,一個Label控件,還有一個Button控件。當用戶點擊Button控件,Composite檢查用戶提交的文本是否包含了XML文件中指定的詞語(XML文件的默認名字是bad_words.xml,通過一個自定義屬性定義),并拋出一個自定義事件。另外,Composite控件還將它的Label子控件的一個Text屬性顯露成頂級屬性。
復合控件可以有選擇地將子控件顯露成屬性,或者有選擇地將子控件的屬性和事件作為頂級屬性和事件顯露出來。當復合控件整合來自子控件的屬性時,它通常只是簡單地委托子控件執行操作,如下面的例子所示:
// 將操作委托給標簽對象,標簽對象是一個 // System.Web.UI.WebControls.Label的實例 Public Property Text() As String Get EnsureChildControls() Return label.Text End Get Set EnsureChildControls() label.Text = value End Set End Property |
我們需要一個文本輸入框讓用戶輸入內容,一個按鈕來提交表單,還要一個向用戶反饋信息的文本標簽。下面我們來看看Web表單的代碼,復合控件就是在這里實例化的:
【composite.aspx】
<%@ page language="vb" debug="false" trace="false" %> <%@ Register TagPrefix="Custom" Namespace="CustomControls" Assembly = "CustomControls" %> <html> <script language="VB" runat=server> Private Sub CheckText(sender As Object, e As CheckEventArgs) If e.Match = false Then Composite.Text = "<h2>發布內容請遵守本站規則!不得發布攻擊性言辭!</h2>" Else Composite.Text = "你提交的內容已通過檢查!" End If End Sub </script> <body> <h1>語言凈化控件實例</h1><br> <form runat=server> <Custom:Composite id = "Composite" OnCheck = "CheckText" filename = "bad_words.xml" runat = server/></form></body> </html> |
上面的代碼首先注冊指定的復合控件。我們將把控件的代碼編譯成一個.dll文件,放入應用的bin目錄,這是ASP.NET首先搜索的位置。在Web表單構成的用戶界面中,我們實例化了自定義控件,同時指定了:
⑴ 當控件拋出OnCheck事件,執行一個本地的子過程CheckText。我們把復合控件的標簽的文本通過一個公用屬性顯露出來,標簽的內容由OnCheck事件句柄設置的另一個公用屬性決定。
⑵ 定義攻擊性詞語的XML文件的名字。
⑶ 另外,我們還定義了一個由復合控件調用的CheckText子過程。
現在來看復合控件本身。復合控件有兩個類,用兩個獨立的VB源文件實現,分別是composite.vb和checkevent.vb。
【composite.vb】
Imports System Imports System.Web Imports System.Web.UI Imports System.Web.UI.WebControls Imports System.Xml Imports System.Collections Namespace CustomControls Public Class Composite Inherits Control Implements INamingContainer Private _filename As String = "bad_words.xml" Private label As Label Private box1 As TextBox Public Property filename() As String Get Return _filename End Get Set _filename = value End Set End Property ' 以用戶提交的文本內容為輸入參數。如果用戶提交的內容包含攻擊性言辭, ' 則返回修改后的版本, ' 否則,直接返回原始的文本。 Public Function CheckString(InputString as String) as string Dim alWordList As new ArrayList dim xmlDocPath as string = mappathsecure("bad_words.xml") dim xmlReader as XmlTextreader = new xmlTextReader(xmlDocPath) dim element as string dim output as string dim asterisks as string = "*************************" ' 將定義攻擊性言辭的xml文件內容讀入到一個ArrayList while (xmlReader.Read()) if xmlReader.NodeType=xmlNodeType.Text then alWordList.Add(xmlReader.Value) end if end while xmlReader.Close() ' 檢查用戶提交的文本內容,將攻擊性言辭替換為適當數量的星號 For Each element in alWordList InputString=InputString.Replace(element, asterisks.substring(1, (element.length))) Next Return InputString End Function Public Property Text() As String Get ' 該方法首先檢查ChildControlsCreated屬性的當前值。如果該值是false, ' 則調用CreateChildControls方法 EnsureChildControls() Return label.Text End Get Set EnsureChildControls() label.Text = value End Set End Property Public Event Check As CheckEventHandler Protected Overridable Sub OnCheck(ce As CheckEventArgs) RaiseEvent Check(Me, ce) End Sub '創建Composite控件的子控件 Protected Overrides Sub CreateChildControls() Controls.Add(New LiteralControl("<h3>請在下面輸入文字內容: ")) '文本輸入框 Dim box1 As New Textbox() box1.Text = "" Controls.Add(box1) Controls.Add(New LiteralControl("</h3>")) '按鈕 Dim button1 As New Button() button1.Text = "提交" Controls.Add(New LiteralControl("<br>")) Controls.Add(button1) ' 將一個事件句柄加入新創建的按鈕對象 AddHandler button1.Click, AddressOf Me.ButtonClicked Controls.Add(New LiteralControl("<br><br>")) label = New Label() label.Height = Unit.Pixel(50) label.Width = Unit.Pixel(500) label.Text = "" Controls.Add(label) End Sub Protected Overrides Sub OnPreRender(e As EventArgs) CType(Controls(1), TextBox).Text = "" End Sub Private Sub ButtonClicked(sender As [Object], e As EventArgs) OnCheck(New CheckEventArgs(CType(Controls(1), TextBox).Text, CheckString(CType(Controls(1), TextBox).Text))) End Sub End Class End Namespace |
上面代碼的主要任務是:
⑴ 首先導入必要的名稱空間,聲明當前類所屬的名稱空間。
⑵ 接下來定義Composite的主體。Composite從最基本的Control類繼承,另外還要實現INamingContainer接口。INamingContainer接口允許Composite控件將事件轉發到它的Button子控件。
⑶ 用CreateChildControls方法(而不是OnInit或構造函數)創建子控件。
⑷ Composite控件沒有顯露出Button子控件的Click事件。相反,它處理了Click事件,并拋出自定義事件Check。
⑸ Composite控件顯露了下列公用屬性:Text,即Label子控件的Text屬性值;FileName,允許獲取和設置定義攻擊性詞語的XML文件的名字
⑹ 主要的檢查功能由CheckString方法實現,它的輸入參數是一個文本字符串。CheckString方法從XML文件讀取禁用的詞語,放入一個數組列表(ArrayList),然后檢查指定的字符串是否包含禁用的詞語。所有“攻擊性”的詞語將被適當數量的“*”替代。
⑺ OnPreRender清除文本框子控件的文本。
⑻ 當用戶點擊按鈕,ButtonClicked開始執行。ButtonClicked調用onCheck子過程,傳入適當的參數(一個新建的CheckEventArgs對象,創建CheckEventArgs對象的參數是檢查前和檢查后的文本)。OnCheck隨后觸發一個事件,該事件將由.aspx頁面中的代碼處理。
【CheckEvent.vb】
' 包含定制事件數據類CheckEventArgs的代碼. ' 另外還定義了Check事件的事件句柄 Imports System Namespace CustomControls Public Class CheckEventArgs Inherits EventArgs Private _match As Boolean = False Public Sub New(string1 As String, string2 as String) If string1=string2 Then _match = True End If End Sub Public ReadOnly Property Match() As Boolean Get Return _match End Get End Property End Class Public Delegate Sub CheckEventHandler(sender As Object, ce As CheckEventArgs) End Namespace |
CheckEventArgs的構造函數是兩個字符串,根據字符串的值設置相應的匹配標記_match。另外,上面的代碼還定義了CheckEventHandler事件句柄。
編寫好上面的代碼后,如果你沒有安裝IDE,用下面的命令執行編譯即可:
vbc /t:library /out:./bin/CustomControls.dll /r:System.dll /r:System.web.dll /r:System.drawing.dll /r:System.Data.dll /r:System.xml.dll *.vb |