ASP.NET设计控件净化网站语言
2024-07-10 12:54:57
供稿:网友
一、概述
考虑一下这种情形:你为一个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