目前,網上論壇各式各樣各種版本不計其數,歸根結底如何神通其核心數據存取倉庫都是“數據庫”,我是指我們常用的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="" fatIE="20" exp="243" homepage="http://www.lshdic.com" email="[email protected]" oicq="21152530" qianming="簽名檔內容" address="山東臨沂"/>
<cike name="孤獨刺客" pass="123" sex="男士" age="20" img="man2.gif" fatIE="0" exp="20" homepage="" email="[email protected]" 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&g
t;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-st
ylesheet節點的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("data\2.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服務器上測試。