一、概述
考虑一下这种情形:你为一个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> |
上面的代码首先注册指定的复合控件。我们将把控件的代码编译成一个.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 property filename() as string 以用户提交的文本内容为输入参数。如果用户提交的内容包含攻击性言辞, 将定义攻击性言辞的xml文件内容读入到一个arraylist 检查用户提交的文本内容,将攻击性言辞替换为适当数量的星号 return inputstring end function public property text() as string public event check as checkeventhandler protected overridable sub oncheck(ce as checkeventargs) 创建composite控件的子控件 controls.add(new literalcontrol(“<h3>请在下面输入文字内容: “)) 文本输入框 controls.add(new literalcontrol(“</h3>”)) 按钮 将一个事件句柄加入新创建的按钮对象 controls.add(new literalcontrol(“<br><br>”)) protected overrides sub onprerender(e as eventargs) private sub buttonclicked(sender as [object], e as eventargs) |
上面代码的主要任务是:
⑴ 首先导入必要的名称空间,声明当前类所属的名称空间。
⑵ 接下来定义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) public readonly property match() as boolean public delegate sub checkeventhandler(sender as object, ce as checkeventargs) |
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 |
