一概述
考虑一下这种情形你为一个Web网站写了一个应用程序它的功能是接受用户的输入并将输入内容永久保存例如保存到数据库另外还要在网站上显示出用户输入的内容例如论坛就是一个很典型的例子
如果用户来源很杂必须考虑如何防止用户提交和张贴攻击性(或者色情的庸俗的)的内容可能的解决方案包括
⑴ 将用户群限制到一个封闭的用户团体即要求用户使用程序功能之前必须先注册/登录这样由于每次提交的内容都可以追查到提交者用户破坏网站规则的可能性就小了很多如果有用户做出了不应该做的事你就可以核实用户身份予以相应的处理
⑵ 在网站上发布用户提交的内容之前先由管理员审阅很多时候由于人力资源有限这个办法不一定行得通
⑶ 禁止用户提交攻击性内容这是最理想的解决办法把问题解决在起源但具体应该怎么实现呢?
本文介绍的方案以一个复合控件为基础利用一个XML文件来定义攻击性词语我们将用VBNET编写这个复合控件用普通的文本编辑器和命令行编译器(vbc)完成整个工程的构建
在正式编写控件之前首先我们来简单地回顾一下ASPNET中控件的概念本文出现的所有控件都是服务器控件它们在服务器上运行将HTML代码发送到客户端要理解控件的分类可以从控件是否嵌入到Web表单页面(因而采用按需编译方式)或预先编译的角度来观察微软定义了下列ASPNET服务器控件HTML服务器控件Web服务器控件验证控件用户控件
前三种控件读者应该已经比较熟悉了对于开发者来说它们是最简单的控件类型在ASPNET中已经由微软为我们编写好用户控件则有所不同用户控件是包装成ascx页面形式的aspx页面其他aspx页面可以通过注册和实例化来调用用户控件的功能这是一种被寄予厚望的服务器端控件对于ASP/ASPNET开发者来说它代表着一大进步特别地现在编写控件的语言已经全面支持面向对象技术
ASPNET用户控件由一个或多个服务器控件静态HTML元素构成可以包含额外的代码每个用户控件封装一组特定的功能用户控件可以通过简单地扩展现有服务器控件(控件组)得到例如带有旋转功能的图形控件在文本框中保存日期的日历控件
二开发复合控件
控件要检查用户提交的内容是否包含攻击性词语攻击性词语由一个XML文件定义XML文件的结构如下
<?xml version=? encoding=GB>
<words>
<word>词语一</word>
<word>词语二</word>
</words>
本文的复合控件(Composite)包含三个ASPNET服务器控件一个Textbox控件一个Label控件还有一个Button控件当用户点击Button控件Composite检查用户提交的文本是否包含了XML文件中指定的词语(XML文件的默认名字是bad_wordsxml通过一个自定义属性定义)并抛出一个自定义事件另外Composite控件还将它的Label子控件的一个Text属性显露成顶级属性
复合控件可以有选择地将子控件显露成属性或者有选择地将子控件的属性和事件作为顶级属性和事件显露出来当复合控件整合来自子控件的属性时它通常只是简单地委托子控件执行操作如下面的例子所示
// 将操作委托给标签对象标签对象是一个
// SystemWebUIWebControlsLabel的实例
Public Property Text() As String
Get
EnsureChildControls()
Return labelText
End Get
Set
EnsureChildControls()
labelText = value
End Set
End Property
我们需要一个文本输入框让用户输入内容一个按钮来提交表单还要一个向用户反馈信息的文本标签下面我们来看看Web表单的代码复合控件就是在这里实例化的
【compositeaspx】
<%@ 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 eMatch = false Then
CompositeText = <h>发布内容请遵守本站规则!不得发布攻击性言辞!</h>
Else
CompositeText = 你提交的内容已通过检查!
End If
End Sub
</script>
<body>
<h>语言净化控件实例</h><br>
<form runat=server>
<Custom:Composite id = Composite OnCheck = CheckText
filename = bad_wordsxml runat = server/></form></body>
</html>
上面的代码首先注册指定的复合控件我们将把控件的代码编译成一个dll文件放入应用的bin目录这是ASPNET首先搜索的位置在Web表单构成的用户界面中我们实例化了自定义控件同时指定了
⑴ 当控件抛出OnCheck事件执行一个本地的子过程CheckText我们把复合控件的标签的文本通过一个公用属性显露出来标签的内容由OnCheck事件句柄设置的另一个公用属性决定
⑵ 定义攻击性词语的XML文件的名字
⑶ 另外我们还定义了一个由复合控件调用的CheckText子过程
现在来看复合控件本身复合控件有两个类用两个独立的VB源文件实现分别是compositevb和checkeventvb
【compositevb】
Imports System
Imports SystemWeb
Imports SystemWebUI
Imports SystemWebUIWebControls
Imports SystemXml
Imports SystemCollections
Namespace CustomControls
Public Class Composite
Inherits Control
Implements INamingContainer
Private _filename As String = bad_wordsxml
Private label As Label
Private box 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_wordsxml)
dim xmlReader as XmlTextreader = new xmlTextReader(xmlDocPath)
dim element as string
dim output as string
dim asterisks as string = *************************
将定义攻击性言辞的xml文件内容读入到一个ArrayList
while (xmlReaderRead())
if xmlReaderNodeType=xmlNodeTypeText then
alWordListAdd(xmlReaderValue)
end if
end while
xmlReaderClose()
检查用户提交的文本内容将攻击性言辞替换为适当数量的星号
For Each element in alWordList
InputString=InputStringReplace(element
asteriskssubstring( (elementlength)))
Next
Return InputString
End Function
Public Property Text() As String
Get
该方法首先检查ChildControlsCreated属性的当前值如果该值是false
则调用CreateChildControls方法
EnsureChildControls()
Return labelText
End Get
Set
EnsureChildControls()
labelText = 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()
ControlsAdd(New LiteralControl(<h>请在下面输入文字内容: ))
文本输入框
Dim box As New Textbox()
boxText =
ControlsAdd(box)
ControlsAdd(New LiteralControl(</h>))
按钮
Dim button As New Button()
buttonText = 提交
ControlsAdd(New LiteralControl(<br>))
ControlsAdd(button)
将一个事件句柄加入新创建的按钮对象
AddHandler buttonClick AddressOf MeButtonClicked
ControlsAdd(New LiteralControl(<br><br>))
label = New Label()
labelHeight = UnitPixel()
labelWidth = UnitPixel()
labelText =
ControlsAdd(label)
End Sub
Protected Overrides Sub OnPreRender(e As EventArgs)
CType(Controls() TextBox)Text =
End Sub
Private Sub ButtonClicked(sender As [Object] e As EventArgs)
OnCheck(New CheckEventArgs(CType(Controls() TextBox)Text
CheckString(CType(Controls() 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页面中的代码处理
【CheckEventvb】
包含定制事件数据类CheckEventArgs的代码
另外还定义了Check事件的事件句柄
Imports System
Namespace CustomControls
Public Class CheckEventArgs
Inherits EventArgs
Private _match As Boolean = False
Public Sub New(string As String string as String)
If string=string 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/CustomControlsdll /r:Systemdll /r:Systemwebdll
/r:Systemdrawingdll /r:SystemDatadll /r:Systemxmldll *vb