起草语:流行论坛(我们重点讨论ASP,ASP。NET论坛)的开发思路
老手可略过此节,就读下节。
目前,网上论坛各式各样各种版本不计其数,归根结底如何神通其核心数据存取仓库都是“数据库”,我是指我们常用的ACCESS SQLSERVER等等这种形式的“数据库”,而后由PHP ASP ASP。NET JSP等等这些后台语言通过中心“ADO,SQL”两条线,像伐木搭桥似的一小块一小块(会员注册、会员登陆、论坛栏目分类、项目统计、单个栏目、帖子浏览、后台管理……)积木似的累积成形式各异的论坛系统,而这一块块的“积木”,如何异样然终归就是对数据库的存取其成分无非就是构成木料的物质不会变成铁的,可见,好的论坛程序并不难写,难得是清晰的思路、优质的“数据库结构”,既然是这样,下面我就将本人着手开发ASP。NET+XML无数据库实现论坛总结的一些经验思路一一陈述,读毕若有兴趣不妨自己动手一试,尝试一下用全新的手段开发论坛的乐趣
第一节:废话不说、解答疑问
1:这篇文章需熟悉哪些知识的人才能读懂?
至少从事学习一年以上WEB后台网站编程综合知识的人即可读懂,熟悉ASP。NET编程及XML文档结构的程序员更能深入意会
2:Xml能充当“数据库”吗?
能,只不过是不同的概念和组织形式
3:用Xml充当数据库制作论坛的开发难度是否比使用ACCESS、SQLSERVER等为数据库的开发难度高?
高许多,甚至有些功能本人水平有限无从下手解决(XML充当数据库相比ACCESS等有一定本质上的局限),但并不代表不可能
4:本文为什么选择ASP。NET做为后台程序语言来结合XML建造论坛?
无特殊意义,ASP、PHP、JSP或其他后台程序语言均可,甚至VB、VC、JAVA等能以软件形式表现论坛
5:两者相比,Xml充当“数据库”做的论坛或其他系统有何好处?
如程序及XML结构精妙完善,无论在大小程序系统上均在
-速度(基于文件,相对忧于ADO存取数据库)
-资源占用(无论数据总和多少,基本是处理分布式的小型文件(通常在1-100K内),无论是在内存或CPU的占用等均忧于数据库)
-分布式(单一的文件集合于文件夹,与将数据集成为一身的数据库,各有优势)
-移植性和通用性(XML本身即是一种通用数据描述语言,无论是在不同操作系统或程序语言上均能顺利移植和应用)
-修改、查找、维护、批量处理(如数据量大,则必须借助程序系统操作,如直接在文件夹管理文件非常麻烦,这方面逊于数据库系统的界面和易用性,除非建立自己的XML数据处理软件或WEB系统弥补这方面的不足)
-安全性(大智慧是取决于程序、加密方面,小聪明是更改后台文件、文件夹的名称和存放位置,只不过基于XML文件构建的数据库结构比较散乱,不如数据表封装的简单)
第二节:引路入门、建立基地(XML数据库结构)
明确做什么样的论坛系统,才能理起思路,这里我们假设开发一简单论坛,其功能功用均效仿常见论坛,我们只要求一个栏目
XML数据库的实现{
会员资料存储(在主目录新建一文件,名为user.xml,该user.xml文件即作为存储所有会员资料的文件,本例结构可是如下形式)[
<?xml version="1.0" encoding="gb2312"?>
<alluser>
<fyw name="风云舞" pass="1234" sex="男士" age="20" img="http://www.lshdic.com/bbs/image/user40.jpg" fatie="20" exp="243" homepage="http://www.lshdic.com" email="lshdic@sina.com" oicq="21152530" qianming="签名档内容" address="山东临沂"/>
<cike name="孤独刺客" pass="123" sex="男士" age="20" img="man2.gif" fatie="0" exp="20" homepage="" email="gdcike@163.com" oicq="" qianming="风云他是猪!吭大爷做关税区的斑竹!" address="山东临沂"/>
<plgirl name="千千纯子" pass="123" sex="女士" age="20" img="woman1.gif" fatie="0" exp="0" homepage="" email="" oicq="" qianming="" address=""/>
</alluser>
]
这是本例所采用的XML存储结构,不难看出一个标签即包含了一个会员的所有信息,标签名即是“用户帐号”,其中name=昵称、pass=密码、sex=性别……,其中“用户帐号”必须保证是唯一的,而且必须限制为英文字母或英文字母后边带数字,关于XML的文件结构及数据一定要求符合XML文档规范及命名规范,若此后在程序中用户所提交数据处理不托,或可导致资料泄露,或该user.xml即宣告报废,必须手工查找更正,否则无法正常存取
论坛帖子资料存储(在主目录新建一文件夹,名为data,该data文件夹今后即保存所有会员发表的帖子,其中帖子的文件名是随机或有规律的如1.xml,2.xml,3.xml,这些文件存储了帖子的所有数据包括主题、回复,当然这些文件的建立、修改、起名、删除等都是在以后用程序实现的,至于新文件起名的程序部分要保证其名称与以有文件互不冲突即可,文件名可以是无任何规律的,毕竟今后我们不以文件名来实现排序,其单个XML文件内容结构如下)[
1.xml:
<?xml version="1.0" encoding="gb2312"?>
<?xml-stylesheet type='text/xsl' href='../file.aspx?dex=1.xml'?>
<document>
<record>
<anthor>fyw</anthor>
<title>帖子标题</title>
<date>2003-12-6 3:27:18</date>
<gengxindate>2003-12-6 3:27:18</gengxindate>
<body>帖子内容
<hr>内容是以过滤HTML等于XML文档规范相冲突的字符以后的合法内容,标题也需要过滤
</body>
</record>
</document>
2.xml:
<?xml version="1.0" encoding="gb2312"?>
<?xml-stylesheet type='text/xsl' href='../file.aspx?dex=7908604.xml'?>
<document>
<record>
<anthor>fyw</anthor>
<title>帖子标题</title>
<date>2003-12-6 3:54:59</date>
<gengxindate>2003-12-6 3:54:59</gengxindate>
<body>帖子内容</body>
<reply>
<anthor>cike</anthor>
<date>2003-12-6 5:53:38</date>
<gengxindate>2003-12-6 5:53:38</gengxindate>
<body>1楼回复的内容</body>
</reply>
<reply>
<anthor>plgirl</anthor>
<date>2003-12-7 5:53:38</date>
<gengxindate>2003-12-7 5:53:38</gengxindate>
<body>2楼回复的内容</body>
</reply>
</record>
</document>
]
这是本例论坛帖子的XML存储结构,现在我们建立起了会员资料存储文件(user.xml)以及论坛帖子存放地点和存放结构(data*.xml),你是否以有所启发?用程序在这两者基石上搭桥引线是否真的能够实现复杂的论坛系统哪?答案是肯定的,只要能通过程序在后台操纵这两个存储基地,加以友好的界面,即可小试牛刀
}
第三节:诸葛布阵、将士磨刀
XML数据存储结构以完成,下面一一列出完成论坛大业所需要的材料
项目包括{
通用函数页
(建立或一或二这种通用函数页或DLL或者是用户控件,将常用的程序过程、变量、函数放入其中,此后使用可大大节省时间避免重复劳动,这在下一章将有所提及)
游客与会员的区分
(即是一个session,本例为session("who")值是否存在的判断,如存在即判断是会员,如不存在值即为游客,我们本例不使用cookies存储用户数据,游客权限自然是只能浏览帖子,不能发表或回复帖子,而会员则可以)
会员注册
(包含各种表单的页面,目的是完成向user.xml添加一新的标签节点(等同添加一新的会员数据),要处理好用户所提交的数据,方法要用到防止跨站提交、会员帐号(即XML节点头标签)要用正则表达式限制为类似程序变量的语法要求或干脆只允许使用英文字母、XML节点属性至少要过滤<>&"'以及回车符号、防止申请的会员帐号以存在user.xml、所有数据英文符号均转换为小写格式即不区分大小写包括用户帐号和密码)
会员登陆
(包含用户帐号输入框、密码输入框和一个登陆按钮,提交后利用XMLDOM搜索user.xml是否有匹配的用户帐号(要特别注意过滤用户提交的数据,尤其是*号等,这在XMLDOM的搜索中被认为是通配符),如user.xml存有该帐号,即将一个session,本例为session("who"),设值为该用户帐号,游客身份即成为会员身份)
会员资料修改
(禁止游客浏览本页,样式保持与会员注册页的表单基本一样(但不包含用户帐号名称修改的功能),只不过所有表单的值均是预读了user.xml中匹配session("who")该会员的信息,用户修改过后,单击完成修改按钮,此时后台程序修改user.xml中该会员对应的节点数据即可)
会员资料显示
(只读页面,读取网址参数中对应的会员帐号,显示会员所有无需保密的信息)
一个论坛栏目主页
(即帖子列表,根据URL页码参数分页,显示data目录中对应页码的所有帖子信息,排序自然根据文件的修改日期,最新更新的最靠顶,这是开发本论坛中最难的一个项目之一,要谨慎处理,最好实现点对点(即1-10,30-40,100-110)形式分页的抽取data目录中的文件,可以保证最优质的运行速度)
帖子显示页
(本例的帖子显示是直接在浏览器访问XML文件,即http://xxx.xxx.xxx/xxx.xml的URL形式访问,由于仅仅使用静态XSL控制XML文件的二层输出形式是远远不够的,所以我们采取的显示结构是三层,其顺序为(1:打开XML文件后查找xml-stylesheet节点的href所指定的XSL文件---2:由于目标文件类型是aspx,我们在目标aspx中根据url参数访问参考对应的XML文件数据,控制数据流输出格式为xsl,丛中很好的整理出理想的xsl样式表,即起到了中间层的关键作用---3:由于aspx输出的xsl是我们在后台整理过的,其高度的智能、合理,即可以配合XML数据输出结构复杂的帖子显示页),期间中间层的开发难度最大,是本论坛系统中最难的一个步骤,简单的地方是XMLDOM分析抽取目标XML中的节点数据,实现显示主题、显示帖子作者、显示帖子内容、显示帖子所有回复,但最难点,本人在着手开发时,就无法解决帖子回复的分页,以及aspx服务器端控件和xsl文档规范相互冲突,可能是本人水平有限,或也是本人原创的这种3层输出存在本质的缺陷,所谓XML实现数据库容易,但通过XSL实现超越HTML的理想输出难)
发表新贴
(禁止游客发表,该项目即是一个输入主题的输入框+内容输入框+提交按钮,可以绑定在栏目帖子列表页下方或新建一个专页,用户提交发表后要用server.HTMLEncode()过滤用户所提交的数据,而后用程序创建XML文件、给XML文件起名、整理XML文件的文件格式如第二节所写的格式、向节点内写入过滤后的数据、修改user.xml对应的发贴会员的节点实现积分+2和发贴数+1,用cookies限制7秒内禁止多次提交灌水)
回复新贴
(禁止游客回复,该页可绑定帖子显示页下方或新建一个专业,提供一内容输入框+提交按钮,提交后如发表新贴过程类同,要进行数据过滤,而后在对应的帖子存储XML文件中插入一reply节点,在该节点中建立用于存储回复作者名称、回复内容、回复日期、回复更新日期等节点,修改user.xml对应的发贴会员的节点实现积分+1和发贴数+1,用cookies限制7秒内禁止多次提交灌水)
版主管理功能
(要实现密码验证,判断禁止游客及会员使用,可选的后台管理功能有奖励会员积分、减少会员积分、删除会员、修改任何帖子、删除任何帖子,甚至可以考虑诸多封IP、置顶、锁定、封版主等等高级功能)
}
第四节:君临沙场、锦囊相助
通过以上三节修炼,君若仔细攻读,定已胸有成竹,跃跃欲试,此时想必已经动起手来,或者是思路很清晰却不知程序该如何写,若真是如此,不必急噪,笔者先将平生所学一些常用“兵法”一一列出,定可使汝茅塞顿开
1:错误提示函数(需要时使用,可弹出对话框给与用户错误提醒,而后自动退回上一页,此函数建议保存为一通用文件,需要时<!--#include file="publicfun.aspx"-->一下即可使用)
<script language=vb runat='server'>
sub t2(tstr2 as string)
response.write (replace("<script language=vbs> msgbox " & chr(34) & tstr2 & chr(34) & ",16," & chr(34) & "错误提示" & chr(34) & ":history.back()</script>","<","<")):response.end
end sub
</script>
2:一般数据检测函数(修改XML标签属性时建议使用,用于一般检测,至于用户帐号等还需要更严密的数据过滤)
<script language=vb runat='server'>
sub jiancha(requeststr as string) '本例过滤'"<>&*,如有疏漏请补之
dim array1(5) as string
array1(0)="'":array1(1)=chr(34):array1(2)="<":array1(3)=">":array1(4)="&":array1(5)="*"
dim tempi as integer
for tempi=0 to ubound(array1)
if instr(requeststr,array1(tempi))<>0 then response.write (replace("<script language=vbs> msgbox " & chr(34) & "参考数据 " & requeststr & " 不可以含有禁止符号 [" & array1(tempi) & "] ,自动返回请更正" & chr(34) & ",16," & chr(34) & "错误提示" & chr(34) & vbcrlf & "history.back()</script>","<","<")):response.end
next
end sub
</script>
3:asp.net拒绝跨站提交注入(可在会员注册、会员修改页等表单较多的页使用)
<%
if lcase(mid(request.ServerVariables("HTTP_REFERER"),8,len(request.ServerVariables("SERVER_NAME"))))<>lcase(request.ServerVariables("SERVER_NAME")) then t2("拒绝跨站提交!")
%>
4:向目标XML文件添加新节点
<%@import namespace='system.xml'%>
<%
dim userdom1=new xmldocument,userdom2,userdom3
userdom1.load (server.mappath("user.xml")) '装载需要操作的XML文件
userdom2=userdom1.selectsinglenode("alluser") '操作游标指向alluser节点,句柄给userdom2变量
userdom3=userdom1.createElement("fyw") '新创建一名为fyw的节点,并将句柄给userdom3变量
userdom3.setAttribute("name","风云舞") '为userdom3添加节点属性,新属性名为name,值为风云舞
userdom3.setAttribute("pass","123")
userdom3.innerText="测试" '设置userdom3节点所包含的数据
userdom2.AppendChild(userdom3) 'userdom2即alluser节点下添加userdom3所描述的fyw节点
userdom1.save (server.mappath("user.xml")) '将变动后的新XML数据保存到user.xml
%>
5:修改XML节点
<%@import namespace='system.xml'%>
<%
dim userdom1=new xmldocument,userdom2
userdom1.load (server.mappath("user.xml"))
userdom2=userdom1.selectsinglenode("alluser").getElementsBytagname("fyw") '操作游标指向alluser节点下的fyw节点
userdom2(0).setAttribute("name","风云舞") '设定fyw节点数组的第一个,将其name属性值修改为风云舞
userdom2(0).innerText="测试" '设定fyw节点数组的第一个,将其所包含的数据改为测试
userdom1.save (server.mappath("user.xml")) '将变动后的新XML数据保存到user.xml
%>
6:查找判断XML节点是否存在
<%@import namespace='system.xml'%>
<%
dim userdom1=new xmldocument
userdom1.load (server.mappath("user.xml")) '装载需要操作的XML文件
'以下正是本人为什么要用“用户帐号”标识用户唯一身份的目的,查找方便呀:)
if userdom1.selectsinglenode("alluser").getElementsBytagname("fyw").count<>0 then t2("fyw节点以存在")
%>
7:批量添加XML节点(在编写回复帖子时,用第4例提到的方法一个一个填加节点很麻烦,这时可以考虑用此方法批量填加)
<%@import namespace='system.xml'%>
<%
dim filedom=new xmldocument,filedom2
filedom.load(server.mappath("data" & request.querystring("dex"))) 'request.querystring("dex")=6.xml
filedom2=filedom.createElement("reply")
'以下这种方法很方便,而且对XML文件的结构排版也很好,合理的空格和回车可以表现良好的XML文档结构
filedom2.innerXml=vbcrlf & " <anthor>" & session("who") & "</anthor>" & vbcrlf & " <date>" & now & "</date>" & vbcrlf & " <gengxindate>" & now & "</gengxindate>" & vbcrlf & " <body>" & neirong.value & "</body>" & vbcrlf & " "
filedom.selectsinglenode("document/record").appendChild(filedom2)
filedom.save(server.mappath("data" & request.querystring("dex"))) 'request.querystring("dex")=6.xml
%>
8:删除XML节点
<%@import namespace='system.xml'%>
<%
dim userdom1=new xmldocument,userdom2
userdom1.load (server.mappath("user.xml"))
userdom1.DocumentElement.RemoveChild (userdom1.selectsinglenode("alluser/fyw")) '删除alluser节点下的所有fyw节点
userdom1.save (server.mappath("user.xml"))
%>
9:创建新的XML文件(可以创建任何类型后缀的文件,不局于XML文件)
<%@import namespace='system.io'%>
<%
dim newfile=new StreamWriter(server.mappath("data" & new DirectoryInfo(server.mappath("data")).getfiles().length+1 & ".xml"),false,System.Text.Encoding.Default)
'以上我们指定默认编码方式为System.Text.encoding.default,或者可改为System.Text.encoding.GetEncoding("gb2312"),不然将以UTF8编码那样XML含有中文就无法正常工作了
newfile.write ("<?xml version=" & chr(34) & "1.0" & chr(34) & " encoding=" & chr(34) & "gb2312" & chr(34) & "?>" & _
vbcrlf & "<?xml-stylesheet type='text/xsl' href='../file.aspx?dex=" & filelength+1 & ".xml'?>" & vbcrlf & _
"<document>" & vbcrlf & " <record>" & vbcrlf & " <anthor>" & session("who") & "</anthor>" & vbcrlf & _
" <title>" & server.HTMLEncode(biaoti.value) & "</title>" & vbcrlf & " <date>" & now & "</date>" & vbcrlf & _
" <gengxindate>" & now & "</gengxindate>" & vbcrlf & " <body>" & server.HTMLEncode(neirong.text) & "</body>" & vbcrlf & _
" </record>" & vbcrlf & "</document>")
newfile.close:newfile=nothing
%>
10:删除指定文件
<%@import namespace='system.io'%>
<%file.delete(server.mappath("data2.xml"))%>
11:用cookies实现7秒内不许重复灌水
<%
if not request.cookies("lshdicbbs") is nothing then
if isdate(request.cookies("lshdicbbs")("guanshui"))=false then response.cookies("lshdicbbs")("guanshui")=now
if datediff("s",request.cookies("lshdicbbs")("guanshui"),now)<7 then response.write ("7秒内禁止重复发贴灌水"):response.cookies("lshdicbbs")("guanshui")=now:response.end
end if
response.cookies("lshdicbbs")("guanshui")=now
%>
12:用正则表达式限制用户提交的数据必须为英文字母(有兴趣可延伸强化为支持英文字母+数字的形式)
<%
dim name1=request.form("username")
if regex.replace(name1,"[a-z]+","",RegexOptions.IgnoreCase)<>"" then t2("帐号名必须使用A-Za-z范围的英文字母")
%>
13:扩展提示工具(早先原创脚本,可根据需要修改)
<div style='position:absolute;left:0;top:0;border-bottom:1 solid green;border-right:1 solid green;border-left:1 solid cccccc;border-top:1 solid cccccc;display:none;z-index:500;background-color:#FFF7FF;padding:2;white-Space:nowrap;table-Layout:fixed;' id=showdiv></div>
<script>
var oldtext="加速变量",colors1=new Array("#FFECD5","#FFF7FF","#FFFFEB","white","#F5FFEB","#EEFAFF","#FFFFEE","#EDFFFC")
function document.onmousemove(){
try{
if(event.srcElement.getAttribute('lshdic'))
{
showdiv.style.left=event.x-3;showdiv.style.top=event.y+document.body.scrollTop+18;if(event.srcElement.lshdic!=oldtext){oldtext=event.srcElement.lshdic;showdiv.innerText=oldtext;showdiv.style.backgroundColor=colors1[Math.round(Math.random()*colors1.length)]};if(showdiv.style.display=='none')showdiv.style.display='
}else{if(showdiv.style.display==')showdiv.style.display='none';}}catch(e){}
}
</script>
<a href="http://www.lshdic.com" lshdic='欢迎访问作者网站'>蓝丽网</a>
<a href="http://www.lshdic.com/bbs" lshdic='欢迎访问蓝丽技术论坛 2003-12-6 10:31:32'>蓝丽技术论坛</a>
第五节:神剑在此、赠我知音
经过以上4个章节的艰苦学习,我相信您以基本了解了如何用后台程序结合XML数据库实现复杂的论坛和系统,但通读N边也起不了实际作用,你必须着手去做,才能在实践中总结出自己的经验,本文章仅仅是个辅助参考而已
由于时间有限,本人只能将亲手开发的ASP。NET版本的XML无数据库论坛(全称:蓝丽NetXml无数据库论坛1.0)奉献给各位研究,由于本人的ASP。NET虚拟空间有点问题,无法提供在线演示,现仅提供全部原代码下载(以上13例原代码多数采摘于本系统中),地址:http://www.lshdic.com/download/netxmlbbs.rar,下载后可参考帮助文件,在您自己的ASP。NET服务器上测试。