Google
搜索WWW 博客内搜索
第一课.用ASP编程实现网络内容快速查找

有一天我突发奇想,要是我每到一个网站,那里都能立刻调出我需要看的信息,那岂非美妙得很。接下来我想更深入地考虑这个问题,坐到椅子上拿一支铅笔,却不知道自己写什么。如此一来,我还是得着手对付代码它们。
我的朋友开了一个小型站点,原本是我设计的。这是个检验我想法的好平台。所以我写出代码,上传了文件。真叫人兴奋,程序工作起来煞是圆满,同时也证明我的想法的确不错。
以前看过一些网络使用者倾向报告,其中有一个规律给我印象很深。说是大多数用户如果在三次点击内无法找到自己需要的内容,就会立刻离开该站点。我的代码就可以针对这个问题,保险用户只要点一次鼠标就找到目标。在我的例子里,假设有个用户上了Yahoo,检索关键字为Fireplace Accessories(飞行器材配件)。在给出的结果里,他来到我朋友的站点,将看到"你正在搜索Fireplace Accessories"等字样的画面。接着他会按照网站上的提示,直接到想要去的页面。
你要作的第一步,是在global.asa文件中建立一个初始变量,放置在你的Sub Session_onStart()程序中。
Sub Session_onStart()
 Referer = Trim(Request.ServerVariables("HTTP_REFERER"))
 If Referer = "" Then
  Referer = "None"
 End If
  Session("Referer") = Referer
End Sub

接着我们来着手看看如何完成程序的主要功能。
因为url经过了编码,所以首先要把来访者主机的指向数据还原成对我们有用的东西。建立一个ASP页面,叫它decode.asp,是我们程序的主干。第一个功能是要对编码过的指向头进行解码。程序有点长,而且很直接,没有什么转弯抹角,如果你嫌麻烦,不如到www.popunet.com的"网虫进修班"上面找到本文,用"拷贝/粘贴"大法吧。
源代码
  第二件要作的事情是:从URL头信息里分离出查询部分--这个才是我们需要的。
 '从指向URL中分离查询字段。
Function isProduct(pStr)
 If pStr  "" And lCase(pStr)  "none" Then
  '向后搜索字段
  temp = inStrRev(pStr, "/")
  '得到目录分离的位置
  tempStr = Right(pStr, temp)
  '得到有关数据长度
  temp2 = Len(pStr)
  '得到查询数据行
  pStr = Mid(pStr, temp, temp2)
  '设定返回功能的值
  isProduct = pStr
Else
  isProduct = ""
End If
End Function

接着是要确立查找的明确标准。为达到这个目的,建立两个静态空间"指?查找指向数据

Function Finder(byRef prodList, byVal refList)
'模糊查询
refList = lCase(refList)
' 通过指针循环查找匹配字段
For i = 0 To uBound(prodList) - 1
If inStr(refList, lCase(prodList(i, 0))) Then
'找到匹配
tHolder = tHolder & "Are You looking For " _
& "" _
& prodList(i, 0) & "
"
End If
'第二次循环
Next
'返回结果
Finder = tHolderEnd Function

通过一个inclue,把我们做好的decode.asp放到需要此功能的任何页面,大功告成啦。
  具体如下:
  '如果指向头不为空,调出此功能If lCase(Session("Referer"))  "none" OR Session("Referer")  "" Then' 解析指向数据 Response.Write vbCrLf & "
" _ & Finder(pArray, URLDecode(isProduct(Session("Referer")))) _ & "
" & vbCrLf
End If

第二课.用ASP编写下载网页中所有资源的程序

看过一篇关于下载网页中图片的文章,它只能下载以http头的图片,我做了些改进,可以下载网页中的所有连接资源,并按照网页中的目录结构建立本地目录,存放资源。
  download.asp?url=你要下载的网页
  download.asp代码如下:
<%
Server.ScriptTimeout=9999
function SaveToFile(from,tofile)
on error resume next
dim geturl,objStream,imgs
geturl=trim(from)
Mybyval=getHTTPstr(geturl)
Set objStream = Server.CreateObject("ADODB.Stream")
objStream.Type =1
objStream.Open
objstream.write Mybyval
objstream.SaveToFile tofile,2
objstream.Close()
set objstream=nothing
if err.number<>0 then err.Clear
end function
function geturlencodel(byval url)'中文文件名转换
Dim i,code
geturlencodel=""
if trim(Url)="" then exit function
for i=1 to len(Url)
code=Asc(mid(Url,i,1))
if code<0 Then code = code + 65536
If code>255 Then
geturlencodel=geturlencodel&"%"&Left(Hex(Code),2)&"%"&Right(Hex(Code),2)
else
geturlencodel=geturlencodel&mid(Url,i,1)
end if
next
end function
function getHTTPPage(url)
on error resume next
dim http
set http=Server.createobject("Msxml2.XMLHTTP")
Http.open "GET",url,false
Http.send()
if Http.readystate<>4 then exit function
getHTTPPage=bytes2BSTR(Http.responseBody)
set http=nothing
if err.number<>0 then err.Clear
end function
Function bytes2BSTR(vIn)
dim strReturn
dim i,ThisCharCode,NextCharCode
strReturn = ""
For i = 1 To LenB(vIn)
ThisCharCode = AscB(MidB(vIn,i,1))
If ThisCharCode < &H80 Then
strReturn = strReturn & Chr(ThisCharCode)
Else
NextCharCode = AscB(MidB(vIn,i+1,1))
strReturn = strReturn & Chr(CLng(ThisCharCode) * &H100 + CInt(NextCharCode))
i = i + 1
End If
Next
bytes2BSTR = strReturn
End Function
function getFileName(byval filename)
if instr(filename,"/")>0 then
fileExt_a=split(filename,"/")
getFileName=lcase(fileExt_a(ubound(fileExt_a)))
if instr(getFileName,"?")>0 then
getFileName=left(getFileName,instr(getFileName,"?")-1)
end if
else
getFileName=filename
end if
end function
function getHTTPstr(url)
on error resume next
dim http
set http=server.createobject("MSXML2.XMLHTTP")
Http.open "GET",url,false
Http.send()
if Http.readystate<>4 then exit function
getHTTPstr=Http.responseBody
set http=nothing
if err.number<>0 then err.Clear
end function
Function CreateDIR(ByVal LocalPath) '建立目录的程序,如果有多级目录,则一级一级的创建
 On Error Resume Next
 LocalPath = Replace(LocalPath, "\", "/")
 Set FileObject = server.CreateObject("Scripting.FileSystemObject")
 patharr = Split(LocalPath, "/")
 path_level = UBound(patharr)
 For I = 0 To path_level
  If I = 0 Then pathtmp = patharr(0) & "/" Else pathtmp = pathtmp & patharr(I) & "/"
   cpath = Left(pathtmp, Len(pathtmp) - 1)
  If Not FileObject.FolderExists(cpath) Then FileObject.CreateFolder cpath
 Next
 Set FileObject = Nothing
 If Err.Number <> 0 Then
  CreateDIR = False
  Err.Clear
 Else
  CreateDIR = True
 End If
End Function
function GetfileExt(byval filename)
 fileExt_a=split(filename,".")
 GetfileExt=lcase(fileExt_a(ubound(fileExt_a)))
end function
function getvirtual(str,path,urlhead)
 if left(str,7)="http://" then
  url=str
 elseif left(str,1)="/" then
  start=instrRev(str,"/")
  if start=1 then
   url="/"
  else
   url=left(str,start)
  end if
  url=urlhead&url
  elseif left(str,3)="../" then
  str1=mid(str,inStrRev(str,"../")+2)
  ar=split(str,"../")
  lv=ubound(ar)+1
  ar=split(path,"/")
  url="/"
  for i=1 to (ubound(ar)-lv)
   url=url&ar(i)
  next
  url=url&str1
  url=urlhead&url
 else
  url=urlhead&str
 end if
 getvirtual=url
end function
'示例代码
dim dlpath
virtual="/downweb/"
truepath=server.MapPath(virtual)
if request("url")<> "" then
 url=request("url")
 fn=getFileName(url)
 urlhead=left(url,(instr(replace(url,"//",""),"/")+1))
 urlpath=replace(left(url,instrRev(url,"/")),urlhead,"")
 strContent = getHTTPPage(url)
 mystr=strContent
 Set objRegExp = New Regexp
 objRegExp.IgnoreCase = True
 objRegExp.Global = True
 objRegExp.Pattern = "(src|href)=.[^\>]+? "
 Set Matches =objRegExp.Execute(strContent)
 For Each Match in Matches
  str=Match.Value
  str=replace(str,"src=","")
  str=replace(str,"href=","")
  str=replace(str,"""","")
 str=replace(str,"'","")
filename=GetfileName(str)
  getRet=getVirtual(str,urlpath,urlhead)
  temp=Replace(getRet,"//","**")
  start=instr(temp,"/")
  endt=instrRev(temp,"/")-start+1
  if start>0 then
   repl=virtual&mid(temp,start)&" "
   'response.Write repl&"<br>"
   mystr=Replace(mystr,str,repl)
  dir=mid(temp,start,endt)
  temp=truepath&Replace(dir,"/","\")
  CreateDir(temp)
  'response.Write getRet&"||"&temp&filename&"<br><br>"
  SaveToFile getRet,temp&filename
 end if
Next
set Matches=nothing
end if
%> 

第三课.4.3 用ASP实现网页BBS

统的网页BBS大多是采用CGI模式实现的,它的实现要求编程者既要掌握编程语言如Perl或C等,又要了解关于CGI模式的各项技术内容,因此要制作自己的网页BBS确实困难不小。ASP(Active Server Pages 动态服务器主页)的出现,使我们眼前一亮,能不能利用ASP实现网页BBS呢?回答当然是肯定的。ASP的诱人之处就在于它提供了一种简单易学的脚本,并带有许多内置的对象,从而提供了一条简捷的编程之路。
  该BBS主要由用户注册(浏览器端)、用户注册(服务器端)、加帖子(浏览器端)、加帖子(服务器端)、帖子具体内容的显示和回复以及所有帖子的显示六个部分组成,其中用户信息存放在数据库author.mdb中,帖子存放在数据库bbs.mdb中。它们均为ACCESS数据库,结构分别如表1、表2所示。
表1 author.mdb
字段名 数据类型 长度 说明
Authname 文本 24 用户名
Password 文本 10 密码
表2 bbs.mdb
字段名  数据类型  长度  说明
ID     文本    4  帖子的编号
Authname  文本    24  用户名
Subject   文本   80   主题
Content   备注      内容
Adddate 日期/时间     加贴的日期
Visitnum  数字  长整型  访问人数
Answernum  文本  4   所回复帖子的编号
(缺省为本身的编号)
Addtime  日期/时间     加贴的时间
Topnum   文本   4   第一层回复帖子的编号
具体实现方法如下所示,其中asp文件和数据库存放在“/hosp/asp"中,其它htm
文件存放在“/hosp"文件中,img文件存放在“/hosp/images"。
1、 用户注册(浏览器端)login.htm:由用户输入相关信息,通过表单传送到服
务器。
<html>
<head><title> register a new user</title><head>
<body>
<FORM METHOD="POST" ACTION="/hosp/asp/register.asp"><P>
<H2>为了标识方便,请您注册一个用户名称</H2>
用户:<INPUT TYPE="TEXT" name="name" SIZE="24"><P>
密码:<INPUT TYPE="password" name="password" SIZE="24"><P>
<INPUT TYPE=SUBMIT VALUE="注 册">
<INPUT TYPE=RESET VALUE="清 除">
<a href="/hosp/asp/dispbbs.asp">返回论坛</a><P>
</body>
</html>

2、 用户注册(服务器端)Register.asp:利用REQUEST.FORM("表单栏位名")接收表单信息,采用ADO技术与数据库author.mdb连接,并将表单信息存入author.mdb中。
<html>
<head><title> 存用户信息 </title></head>
<body>
<!-- 取客户浏览器输入的用户名和密码 -->
<% name=REQUEST.FORM("name")
code=REQUEST.FORM("password")
<!-- 与author.mdb连接 -->
Set Connection=Server.CreateObject("ADODB.Connection")
Connection.Open "author"
Set RS=Connection.Execute("SELECT * FROM author")
<!-- 如果该用户名已存在,请重输,否则存入数据库 -->
If Not RS.EOF Then %>
该用户名已被注册,请您重新<a href="/hosp/login.htm">注册</a>新用户名!
<% Else
Connection.Execute("INSERT INTO author(authname,password) Values('" &name& "','" &code& "')")
RS.Close
Connection.Close %>
<CENTER><B><% =name %></B> 您已注册成功!<P>
<a href="/hosp/asp/dispbbs.asp">返回论坛</a></CENTER>
<% End If %>
</body>
</html>

 
3、 加帖子(浏览器端)bbs_add.htm:由用户输入待加入帖子的相关信息,并传到服务器。
<html>
<head><title> a sample form </title></head>
<body background="/hosp/images/backgrnd.gif">
<FORM METHOD="POST" ACTION="/hosp/asp/bbs.asp"><P>
姓名: <INPUT NAME="name" SIZE="28"> 密码: <INPUT TYPE="password" NAME="code" SIZE="28"><P>
主题:<INPUT NAME="subject" SIZE="66"><P>
内容:<P>
<TEXTAREA NAME="content" ROWS=20 COLS=72></TEXTAREA><P>
<CENTER><INPUT TYPE=SUBMIT VALUE="发布信息">
<INPUT TYPE=RESET VALUE="清除信息"></CENTER>
</FORM>
</body>
</html>

4、 加帖子(服务器端)bbs.asp:接收帖子内容,并存入bbs.mdb中。
<html>
<head><title>bbs.asp</title></head>
<body>
<% name=REQUEST.FORM("name")
code=REQUEST.FORM("code")
subject=REQUEST.FORM("subject")
content=REQUEST.FORM("content")
curdate=Date
curtime=Time
<!-- 与author.mdb连接 -->
Set Connection = Server.CreateObject("ADODB.Connection")
Connection.Open "author"
Set RS = Connection.Execute("SELECT * FROM author WHERE authname='" &name& "' AND password='" &code& "'")
<!-- 判断用户名与密码是否匹配 -->
If Not RS.EOF Then
<!-- 打开数据文件,该文件存放帖子的编号,每加一个帖子,编号加1 -->
Set FileObject = Server.CreateObject("Scripting.FileSystemObject")
Set InStream = FileObject.OpenTextFile ("c:\inetpub\wwwroot\hosp\asp\data.id", 1, False, False)
number = Cstr(InStream.Readline+1)
Set OutStream = FileObject.CreateTextFile ("c:\inetpub\wwwroot\hosp\asp\data.id", True, False)
OutStream.WriteLine number
If Request.QueryString("ID")="" Then
manswernum=number
Else
manswernum=Request.QueryString("ID")
End If
If Request.QueryString("topnum")="" Then
mtopnum=number
Else
mtopnum=Request.QueryString("topnum")
End If
Set OutStream = Nothing
<!-- 与bbs.mdb连接,将帖子信息存入数据库中 -->
Set Connbbs = Server.CreateObject("ADODB.Connection")
Connbbs.Open "bbs"
Connbbs.Execute("INSERT INTO bbs(ID,authname,subject,content,adddate,addtime,answernum,topnum,visitnum) Values('" &number& "','"
&name& "','" &subject& "','" &content& "','" &curdate& "','" &curtime& "','" &manswernum& "','" &mtopnum& "',0)")
Connbbs.Close %>
<% =curdate & " " %><% =curtime & " 添加贴子 " %>
<a href="http://nt-server/hosp/asp/dispbbs.asp">返回论坛</a><P>
姓名:<% =name %><P>
主题:<% =subject %><P>
内容:<% =content %><P>
<% Else %>
您的用户名或密码出错,请您重输!
<% End If
RS.Close
Connection.Close %>
</body>
</html>

5、帖子具体内容的显示和回复 detail.asp
<html>
<head><title>bbs.asp</title></head>
<body background="/hosp/images/backgrnd.gif">
<% idnum=Request.QueryString("ID")
Set Connection=Server.CreateObject("ADODB.Connection")
Connection.Open "bbs"
Set RS=Connection.Execute("SELECT * FROM bbs WHERE ID='" &idnum& "'") %>
姓名:<% =RS(1) %><p>
主题:<% =RS(2) %><p>
内容:<% =RS(3) %><p>
------------------------------------------------------------------------------
<! --形成回复表单 -->
<% resub="Re:" & RS(2)
mtopnum=RS(8)
RS.Close
Connection.Close %>
<FORM METHOD="POST" ACTION="/hosp/asp/bbs.asp?ID=<% =idnum %>&topnum=<% =mtopnum %>"><P>
姓名: <INPUT NAME="name" SIZE="28"> 密码: <INPUT TYPE="password" NAME="code" SIZE="28"><P>
主题:<INPUT NAME="subject" SIZE="66" VALUE="<% =resub %>"><P>
内容:<P>
<TEXTAREA NAME="content" ROWS=20 COLS=72></TEXTAREA><P>
<CENTER><INPUT TYPE=SUBMIT VALUE="信息回复">
<INPUT TYPE=RESET VALUE="清除信息"></CENTER>
</FORM>
<a href="http://nt-server/hosp/asp/dispbbs.asp">返回论坛</a>
</body>
</html>

6、所有帖子的显示 Dispbbs.asp:列出所有的帖子的主题、作者、时间、访问人数等信息。
<html>
<head><title>dispbbs.asp</title></head>
<body background="/hosp/images/backgrnd.gif">
<CENTER><IMG SRC="/hosp/images/luntan.gif" BORDER=0></CENTER><P>
<CENTER><A HREF="/hosp/login.htm"><IMG SRC="/hosp/images/zhuce.gif" BORDER=0></A>
<A HREF="/hosp/bbs_add.htm"><IMG SRC="/hosp/images/jiatz.gif" BORDER=0></A>
<A HREF="/hosp/default.htm"><IMG SRC="/hosp/images/return.gif" BORDER=0></A></CENTER><P>
<% Set Connection=Server.CreateObject("ADODB.Connection")
Connection.Open "bbs"
Set RS=Connection.Execute("SELECT * FROM bbs WHERE ID=answernum ORDER BY ID DESC")
Set Conn=Server.CreateObject("ADODB.Connection")
Conn.Open "bbs"
Set RSNEXT=Conn.Execute("SELECT * FROM bbs")
Do While not RS.EOF %>
<% =RS(0) %> <a href="/hosp/asp/detail.asp?ID=<% =RS(0) %>"><% =RS(2) %></a> -<FONT SIZE="4"><B><% =RS(1) %></B></FONT> <%
=RS(4) &" " %><% =RS(7) %>(<% =RS(5) %>)<P>
<% Set RSNEXT=Conn.Execute("SELECT * FROM bbs WHERE topnum<>ID AND opnum='" &RS(0)& "' ORDER BY ID ASC")
Do While Not RSNEXT.EOF %>
<% =RSNEXT(0) %> <a href="/hosp/asp/detail.asp?ID=<% =RSNEXT(0) %>">Re<% RSNEXT(6) %>:<% =RSNEXT(2) %></a>
-<FONT SIZE="4"><B><% =RSNEXT(1) %></B></FONT> <% =RSNEXT(4) &" " %><% =RSNEXT(7) %>(<% =RSNEXT(5) %>)<P>
<% RSNEXT.MoveNext
Loop
RS.MoveNext
Loop
RSNEXT.Close
Conn.Close
RS.Close
Connection.Close %>
</body>
</html>

第四课.创建移动Web应用程序

介绍
现今移动设备已成为我们生活中的一部分,我们当中的许多人已离不开它们。当这些移动设备连接到Internet时,移动设备的力量将无穷无尽。我们可以在任何时间地点给用户发送数据。典型的移动应用程序是在服务器上使用WML, WMLScript和WBMP开发的。
对于动态WML应用程序,开发者可以使用ASP, JSP, PHP等等。移动设备包括蜂窝电话、寻呼机、掌中浏览器、袖珍PC和车载PC。这些设备中少数支持WML,少数支持HTML,更少数支持同时支持WML和HTML。如果你想确保你的应用程序能在大多数的移动设备中使用,你必须以WML和有限的HTML创建应用程序。
最近Microsoft提出了一个革命性的应用程序开发平台—.NET 框架。.NET 框架包括用于Web开发的ASP.NET(下一版本的ASP)。基于ASP.NET的Web开发即支持传统的Web客户端如IE和Netscape,又支持移动客户端如Phone.com, Nokia 蜂窝电话和袖珍PC。ASP.NET应用程序可以在任何.NET支持的语言环境下如VB.NET(下一版本的VB),JScript, C++, C#等等语言环境下进行开发。在这篇文章中,我们将看到如何使用.NET Mobile Web SDK, ASP.NET和 VB.NET看法移动应用程序。
.NET介绍
在深入之前,我想给你简要地介绍一下.NET。.NET是一个由Microsoft在2000年专业开发者会议上提出的发展中的开发平台。下面的图表展示了.NET 框架应用程序开发的简单结构。


当一个ASP.NET的页面请求从一个Web客户端如IE或移动设备发出时,IIS会收集请求并把它传递给.NET 框架。如果ASP.NET的页面是首次请求,那么.NET 框架将把ASP.NET编译成中间语言(intermediate language,IL)。然后IL代码将被即时(Just-In-Time,JIT)编译器编译成本地代码。就象你所看到的,.NET 框架是整个ASP.NET应用的核心。.NET 框架将提供必须的资源和你所选择由于编译代码的语言编译器。
开发所必须的条件
要使用.NET开发移动应用程序,你必须下载和安装如下的组件。IE和.NET SDK可以从 Microsoft MSDN site 免费下载。
1. Windows 2000 Professional/Server/Advanced Server OS
2. Internet Explorer 5.5
3. .NET framework Beta 1
4. .NET Mobile Web SDK Beta 1
5. WAP模拟器
让我们开始吧!
要使用.NET开发移动应用程序,必须包含由.NET Mobile Web SDK提供(通过MobileUI.DLL文件)的 Mobile Web名域(System.Mobile.UI)。对于那些对namespace(名域)一词感到陌生的人来说,名域就象C++的头文件和Visual Basic的引用。名域将提供开发应用程序所要求的类。
使用.NET 框架开发移动应用程序是很容易的。
1. 创建一个ASP.NET页面
2. 包含Mobile Web Namespace--System.Mobile.UI
3. 用Mobile控件设计页面
就是那么简单。
Mobile 控件
Mobile控件是为开发移动应用程序而设计好的程序块。对于那些喜欢使用象VB和Frontpage等这样的集成开发环境(IDE)来开发GUI/Web的人来说,Mobile 控件就与HTML的按钮控件和VB的Grid控件相类似。Mobile控件通过提供程序员所需要的功能来简化程序的开发过程。
例如,如果你想画一个WML Card标记符,你可以使用Mobile Form控件。对于所有的WML标记符都提供了相应的Mobile 控件。Mobile 控件吸引人之处就在于,它们不仅限用于WML标记符;相反,它们超越了WML标记符,可以创建有用的Mobile 控件,如在WML无法实现的日历Mobile 控件。
让我们看一看一个显示“Hello World!”的ASP.NET页面例子。
〈%@ Page Inherits="System.Mobile.UI.MobilePage"〉
〈%@ Register TagPrefix="Mobile" Namespace="System.Mobile.UI"〉
〈Mobile:Form runat="server"〉
〈Mobile:Label runat="server"〉Hello, World!〈/Mobile:Label〉
〈/Mobile:Form〉


循序渐进—理解ASP.NET页面
1. 在第一行里,给ASP.NET页面对象继承了Mobile Web名域。如果不这样做,ASP.NET就会遵循普通Web应用程序的页面处理程序。
2. 在第二行里,为Mobile Web名域注册了一个标记符前缀。你可以看到在剩下的代码中,对所有的Mobile 控件都使用“Moblie”这个关键字作为名域前缀。你可以使用任何你想的名字作为标记符前缀。
3. 在第三行里,创建一个Mobile窗体并把它的runat属性设置为server。
4. 在第四行里,创建一个Mobile label控件和“Hello World”文本。
5. 最后一行,关闭Mobile窗体标记符
运行以上例子,观看结果。


以上就是例子在Phone.com模拟器显示的效果。现在再让我们看一看ASP.NET生成的代码:


是不是很Cool?ASP.NET为我们生成代码,Mobile Form控件已经被转换为WML card标记符;同样地,Mobile Label控件被转换为WML paragraph标记符。
让我们看一看在Pocket IE中又会怎样:


再让我们看一看ASP.NET为Pocket IE生成的代码:
〈html〉
〈body〉
〈form id="ctrl1" name="ctrl1" method="post" action="Exp1.Aspx?631169274439268880"〉
〈div〉Hello, World!〈/div〉
〈/form〉
〈/body〉
〈/html〉

ASP.NET为Pocket IE生成HTML代码。的确,Mobile 控件有能力探测到所使用的浏览器并生成所支持的代码。所以,当在WAP模拟器中打开一个ASP.NET页面时得到的是WML代码。而在袖珍PC中打开时得到HTML代码。这样就给我们开发一次服务任何移动设备的能力。很Cool,不是吗?
移动应用程序设计概念
NET Mobile Web SDK提供了三个容器对象:MobilePage, Form 和Panel。MobilePage控件是移动应用程序的重要容器。一个单独的MobilePage可以有一个或多个Form控件。一个Form控件可以有0个或多个Panel控件。Panel控件用于给各种Mobile控件分组。
.NET Mobile Web SDK 中Mobile控件
Mobile控件可以被分为三个主要的组。它们是用户界面(UI)控件,验证(Validation)控件和功能(Utility)控件。用户界面控件是如Lable控件一样允许用户控制用户界面的一组控件。验证控件允许我们验证用户的输入如RequiredFieldValidator控件,这些控件在向服务器发送数据之前验证用户输入的数据。功能控件是诸如日历控件这一类的控件。
以下表格展示了以上各个类别的控件。
UI控件
控件名描述Command一个Command控件执行诸如提交信息之类的动作FormForm控件是一个或多个mobile控件的容器Image在移动设备中显示图象Label在移动设备中显示输出文本Link在窗体中创建一个超链接List在移动设备中显示一个选项列表MobilePage控制所有mobile控件的容器TextBox显示单行的文本框Textview显示多行的文本框
验证控件
控件名描述CompareValidator比较两个mobile控件CustomValidator自定义的检验器允许从控件中调用自己的检验过程RangeValidator限制检验器在一个范围内检测数据RegularExpressionValidator检测与控件指定的表达式不符的数据RequiredFieldValidator检测用户在这一区域输入的数据ValidationSummary显示一个所有发生的检验过程的报告
功能控件
控件名描述Call拨打一个电话号码Calendar显示一个日历AdRotator随机显示一个广告
自动分页是.NET Mobile Web SDK的优势之一。.NET远行时会处理目标设备的页面显示问题。对于不同的Mobile控件会采取不同的分页处理形式。例如,对于List控件,分页由该控件的PageSize属性处理。这个属性根据目标设备自动设置。当在运行时显示页面时,panel控件里的控件就会呆在一起。

一个简单的例子
让我们写一个简单应用程序,显示一个文本框接受输入并在下一个窗体中显示。
〈%@ Page Inherits="System.Mobile.UI.MobilePage" %〉
〈%@ Register TagPrefix="mobile" Namespace="System.Mobile.UI" %〉
〈Script language="VB" runat="server"〉
Sub Btn_OnClick(Src As Object, E As EventArgs)
‘move to the next mobile form
ActiveForm = frm2
‘display the name.
EnteredName.Text = "Your name is: " & YourName.Text
End Sub
〈/Script〉
〈mobile:Form id="frm1" runat=server〉
〈mobile:Label runat=server〉Your Name:〈/mobile:Label〉
〈mobile:TextBox runat="server" id="YourName" /〉
〈mobile:Command runat="server" id="btn" 〉Ok
〈/mobile:Form〉
〈mobile:Form id="frm2" runat=server〉
〈mobile:Label runat="server" id="EnteredName" /〉
〈/mobile:Form〉

在以上的代码中,创建了两个窗体。第一个窗体的id为frm1, 第一个窗体的idfrm2。之所以使用这个方法是因为Mobile Form控件不支持name属性,而支持id属性。ASP.NET运行时就是通过这样的方法来确认窗体的。在第一个窗体中,添加了一个lable控件,一个textbox控件和一个button控件。当点击button时,服务器端的VB子程序(Btn_OnClick)就被调用。这对于那些熟悉VB的人来说就象在家里一样亲切。在处理VB子程序的事件里,通过给frm2设置ActiveForm方法来跳到下一个窗体。然后访问定义在frm2中lable控件并设置用户提交的值。
以下就是以上代码在Phone.com模拟器和Pocket IE模拟器中的显示屏幕:






以下是ASP.NET为Phone.com模拟器生成的代码:




就象你所看到的ASP.NET生成白药的WML代码来处理用户的文本输入并发送到服务器端。它同样也生成WML代码以在下一个WML card中显示用户输入的名字。

.NET Mobile Web SDK的优势与限制
这里有一些使用Mobile Web SDK的优势与限制。
优势:
1. 不需要检测浏览器和根据目标设备发送WML或HTML
2. 要学的只是ASP.NET和.NET Mobile控件,而不必学WML
3. Visual Studio.NET易用的编程模式拖放的应用程序开发
4. 支持服务器端事件处理
5. 支持自动分页
限制:
1. 只限在Microsoft产品和操作系统中使用
2. 当WML或HTML的新版本发布,你只有等Microsoft宣布它的新版Mobile Web控件的发布
支持的设备
.NET框架还在发展之中。目前,.NET Mobile Web SDK已经在如下的设备中进行了完全的测试:
内嵌Microsoft Pocket Internet Explorer 4.5的袖珍PC
Mitsubishi T250移动电话
Nokia 7110移动电话
Nokia 7110的Nokia WAP Toolkit 2.0 Beta 模拟器
.NET Mobile Web SDK在如下的准备中进行过有限的测试:
Samsung触点移动电话
Sony CMD-z5移动电话
Microsoft Mobile Explorer v 2.01 模拟器
l Phone.com UP.SDK 4.0模拟器
Phone.com UP.SDK 3.2 for WML模拟器
还有许多其他的支持设备,但是没有在.NET Mobile Web SDK条件下进行过测试。
总结
在这篇文章中展示了.NET Mobile Web SDK的性能和NET Mobile Web SDK如何支持不同的设备。也看到了一个使用.NET Mobile Web SDK生成移动应用程序的例子。想获得更多的关于. NET Mobile Web SDK的信息,请查看以下的相关链接。


第五课.在服务器端控制网页

使用.Net和C#开发Web应用程序往往能给我们很大的启示,尤其在开发相对简单的例行任务时就更是如此。例如,在许多时候,我们都需要有条件地显示一个网页的一部分。需要这么做的原因有许多,例如,根据用户的角色,有一部分是它不应当看到的。或者,我们也可以考虑搜索功能,只有点击了一个链接后,搜索选项才是可用的。
   我们先来解释一下解决这一问题的方法,然后再详细地解释所使用的代码。在.Net中开发Web应用程序,我们既可以使用服务器端的Web控件(Web表单)也可以使用HTML控件。使用.NET开发Web应用程序的重点是服务器端控件。我们在本文中就使用了Web表单服务器端控件。
  Panel是一个服务器端的Web表单控件,一个Panel控件就是HTML网页上的一个矩形区域,它是否可见可以在服务器端进行控制。因此,首先,我们可以从将HTML代码段放在一个Panel控件中,HTML代码段可以由服务器端控件和客户端控件组成。一旦我们将Panel控件的visibility属性设置为“false”,则整个HTML代码段就成为不可见的了。其次,我们可以使用名字为LinkButton的另一个Web表单控件,它本质上是一个超级链接,但通过服务器端的OnClick方法,它可以起到按钮的作用。在这种方法中,我们只要简单地访问Panel对象的C#语言表示,并将其visibility属性设置为true或false,网页就会自动地刷新自己。
  在.NET中开发Web应用程序的一个重要差别是,网页上的每个控件都被表示为服务器端的一个.NET对象,而且这些对象(控件)的状态通过与服务器间的多次交互来维护,这就使我们能够对服务器端和客户端的事件作出反应。响应服务器端的事件时,百网页重新刷新时,其内部的所有对象(控件)也都会得到刷新。我们无需再绘制任何控件,这一切都是自动完成的。这种方法最有吸引力的是一个好的面向对象编程人员能够在一种编程语言模式中工作,而且可以方便地使用JavaScript,调试也非常简便。这种方法的一个小问题就是它不能使用FrontPage或Dreamweaver等HTML代码编辑器。一旦这个问题得到了解决,服务器端的编程模式就更完善了。
  下面是编写本文中例子代码所需要的步骤:
   ·为HTML代码段获得Panel控件。
   ·将HTML代码置入Panel控件中。
   ·创建一个LinkButton控件。
   ·提供一个onClick函数。
   ·在按钮的点击函数中隐藏/显示Panel控件。
  1、为HTML代码段获得Panel控件

   打开网页(.aspx)的设计视图,并选择“Webforms控件”工具箱,从工具箱中拖出一个Panel控件到HTML网页的设计视图上。这时就会看到一个矩形框,发改变它的大小,直到能够容下你想输入的HTML代码段。
   下面是HTML设计视图中Panel控件的定义:
<TABLE cellSpacing=0 cellPadding=0 width=600 bgColor=#ffffff border=0>
<?xml:namespace prefix = asp />
<asp:panel id=TestPanel Width="398px" Height="171px" runat="server">
HTML GOES HERE
</asp:panel>
</TABLE>

  2、将我们的HTML代码输入到Panel控件中

   在Panel控件中编写相关的HTML代码(或将HTML代码拖到Panel控件中),下面是一个例子:
<asp:panel id=TestPanel Width="398px" Height="171px" runat="server">
<H2>An example HTML segment that is going to disappear </H2>

An example drop down <asp:DropDownList id=ADropDownListBox runat="server">
</asp:DropDownList>
</asp:panel>

  3、创建一个LinkButton控件

   我们需要对这一部分作一些解释。我们为什么会用LinkButton控件取代一个超级链接呢?超级链接意味着我们可以随意到包括当前的网页在内的任意网页上,但并不意味着我们要返回正在修改的网页。另外,也没有象OnClick这样能够处理超级链接的服务器端方法。
   LinkButton的外观和风格与hyperlink相同,但它还有另外二点好处,即:
   ·点击时能够返回同一个网页。
   ·便于使用的OnClick方法。
   下面是一个LinkButton控件定义的例子:
<asp:LinkButton id=ChangeAppearanceButton runat="server">
Change Appearance
</asp:LinkButton>


  4、提供onClick函数

   如果双击LinkButton控件,IDE就会自动地将我们引到服务器端该控件的OnClick方法处,在这里我们就可以编写隐藏Panel控件的代码。
   5、在按钮的OnClick方法中隐藏/显示Panel控件

   下面是OnClick方法的一个例子:
private void ChangeAppearanceButton_Click(object sender, System.EventArgs e)
{
this.TestPanel.Visible = this.TestPanel.Visible ? false : true;
}

  上面的代码是一个极好的知道如何维护自己状态的用品端编程模式的例子。当用户点击链接时,就会执行上面的代码,但开发人员并没有改变HTML网页的其他部分,控件本身知道如何刷新它们自己。
   结论
   下面是我从.NET模式中精选出来的非常有趣的特性:
   ·服务器端编程模式。
   ·Web表单的状态是自动维护的。
   ·高度一致的前、后端统一对象编程模式。
   ·用处很大的IDE能够提示每个方法和可能的参数。
   ·在网页的HTML视图中编写XML代码也有提示。
   ·配置所需要的统一的web.config。
   尽管本文中的例子相当简单,即使使用传统的方法也能够非常简单地实现,但这种服务器端的编程模式说明了NET的一个有趣的特性,即能够自动维护其状态的一致的统一服务器端编程模式。
第六课.Asp及Web开发中的常见问题

表格的折行处理.
<table style="TABLE-LAYOUT: fixed" width="200" border="0" cellspacing="0" cellpadding="7" bgcolor="#f7f7f7">
<tr>
<td style="LEFT: 0px; WIDTH: 100%; WORD-WRAP: break-word">
dffadfdaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsfdffadfdasfdffadfdasfdffadfdasfdffadfdasfdffadfdasfd
ffadfdasfdffadfdasfdffadfdasfdffadfdasf
</td>
</tr>
</table>

此处主要是用css样式进行控制在<table>标签中有个style="TABLE-LAYOUT: fixed",其说明如下
语法:
table-layout : auto | fixed
参数:
auto :  默认的自动算法。布局将基于各单元格的内容。表格在每一单元格读取计算之后才会显示出来。速度很慢
fixed :  固定布局的算法。在这算法中,水平布局是仅仅基于表格的宽度,表格边框的宽度,单元格间距,列的宽度,而和表格内容无关,此时在<td>标签中如果没有WORD-WRAP: break-word样式,表格中的内容将只能显示一部份,具体看表格的宽度了.word-wrap说明如下:
语法:
word-wrap : normal | break-word
参数:
normal :  允许内容顶开指定的容器边界
break-word :  内容将在边界内换行。如果需要,词内换行(word-break)也会发生
VBSCRIPT标记索引
基本运算
+ 数字加法及字符串连接
- 数字减法
* 数字乘法
/ 数字除法
Mod 求余数
\ 求商数
& 字符串连接
^ 次方
= 相等
<> 不相等
>= 大于或等于
> 大于
<= 小于或等于
< 小于
Not 非
And 且
Or 或
Xor 异或
循环及决策
if ....then 若...则...
if ...then...else 若...则...非
else if... 非若
select case... 群组选择条件
end select
for ... next 计数循环
while...wend 条件循环(一)
do while...loop 条件循环(二)
do...loop while 条件循环(三)
do until...loop 条件循环(四)
do...loop until 条件循环(五)
数学函数
Abs 绝对值
Sgn 正负号
Hex 转换成十六进制
Oct 转换成八进制
Sqr 平方根
Int 取整数
Fix 取整数
Round 取整数
Log 以e为底的对数
Sin 正弦函数
Cos 余弦函数
Tan 正切函数
字符串处理函数
Len 字符串长度
Mid 取部分字符串
Left 从字符串开头取部分字符串
Right 从字符串结尾取部分字符串
Lcase 转换成小写
Ucase 转换成大写
Trim 清除字符串开头及结尾的空格符
Ltrim 清除字符串开头空格符
Rtrim 清除字符串结尾空格符
Replace 替换字符串部分字符
Instr 判断是否包含于另一个字符串(从起始搜寻)
InstrRev 判断是否包含于另一个字符串(从结尾搜寻)
Space 任意字符数的空格符
String 任意字符数的任一字符
StrReverse 反转字符串
Split 以某字符分割字符串
数据类型转换函数
Cint 转换成整形
Cstr 转换成字符串
Clng 转换成常整数
Cbool 转换成布尔函数
Cdate 转换成日期函数
CSng 转换成单精度
CDbl 转换成双精度
日期时间函数
Date 现在日期
Time 现在时间
NOw 现在日期时间
DateAdd 增加日期
DateDiff 两日期差
DateSerial 日期设定
DateValue 日期设定
Year 现在年份
Month 现在月份
Day 现在天
Hour 现在时刻
Minute 现在分钟
Second 现在秒钟
Timer 午夜距现在秒数
TimeSerial 时间设定
TimeValue 时间所属部分
WeekDay 星期名称
MonthName 月份名称
其它函数
Array 产生数组
Asc 字符ASCII码
Chr ASCII码字符
Filter 过滤数组
InputBox 输入窗口
Join 合并数组中的元素
MsgBox 信息窗口
Lbound 数组下界
Ubound 数组上界
指令
Const 设定常数
Dim 定义变量或者数组
Erase 清除数组
ReDim 重新声明数组
Randomize 起始随机数
Rnd 取得随机数
ASP对象
Session对象
IsEmpty 测试Session变量是否存在
TimeOut 设定Session变量生存周期
Abandon 强制清除Session变量
Application对象
IsEmpty 测试Application变量是否存在
Lock 锁定Application变量
Unlock 解除Lock指令的锁定
Cookies对象
Expires 设定Cookies变量的生存周期
Connection对象
Open 打开与数据库的连接
Execute 打开Recordset对象
Close 关闭Connection对象
Recordset对象
movefirst 将记录指针移至第一条
movelast 将记录指针移至最后一条
movenext 将记录指针移至下一条
moveprevious 将记录指针移至上一条
bof 测试是否为recordset的起始
eof 测试是否为recordset的结束
open 打开Recoreset对象
close 关闭recordset对象
fields 读取数据的子对象
fileds.count 字段个数
pagesize 每页记录条数
absolutepage 设定为某页
pagecount 总页数
Absoluteposition 直接跳至某条记录
MicrosoftVBscript运行时错误列表(10进制表示)
error # 5 无效的过程调用或参数
error # 5 无效的过程调用或参数
error # 6 溢出
error # 7 内存不够
error # 9 下标越界
error # 10 该数组为定长的或临时被锁定
error # 11 被零除
error # 13 类型不匹配
error # 14 字符串空间不够
error # 17 不能执行所需的操作
error # 28 堆栈溢出
error # 35 未定义过程或函数
error # 48 加载 DLL 时出错
error # 51 内部错误
error # 52 错误的文件名或号码
error # 53 文件未找到
error # 54 错误的文件模式
error # 55 文件已经打开
error # 57 设备 I/O 错误
error # 58 文件已存在
error # 61 磁盘已满
error # 62 输入超出了文件尾
error # 67 文件过多
error # 68 设备不可用
error # 70 没有权限
error # 71 磁盘没有准备好
error # 74 重命名时不能带有其他驱动器符号
error # 75 路径/文件访问错误
error # 76 路径未找到
error # 91 对象变量未设置
error # 92 For 循环未初始化
error # 94 无效使用 Null
error # 322 不能创建必要的临时文件
error # 424 缺少对象
error # 429 ActiveX 部件不能创建对象
error # 430 类不支持 Automation 操作
error # 432 Automation 操作中文件名或类名未找到
error # 438 对象不支持此属性或方法
error # 440 Automation 操作错误
error # 445 对象不支持此操作
error # 446 对象不支持已命名参数
error # 447 对象不支持当前区域设置选项
error # 448 未找到已命名参数
error # 449 参数是必选项
error # 450 错误的参数个数或无效的参数属性值
error # 451 对象不是一个集合
error # 453 未找到指定的 DLL 函数
error # 455 代码资源锁定错误
error # 457 此键已与该集合的一个元素关联
error # 458 变量使用了一个 VBScript 中不支持的 Automation 类型
error # 462 远程服务器不存在或不可用
error # 481 无效图片
error # 500 变量未定义
error # 501 非法赋值
error # 502 对象不能安全用 Script 编程
error # 503 对象不能安全初始化
error # 504 对象不能安全创建
error # 505 无效的或无资格的引用
error # 506 类没有被定义
error # 507 出现一个意外错误
error # 1001 内存不够
error # 1002 语法错误
error # 1003 缺少 ':'
error # 1005 缺少 '('
error # 1006 缺少 ')'
error # 1007 缺少 ']'
error # 1010 缺少标识符
error # 1011 缺少 '='
error # 1012 缺少 'If'
error # 1013 缺少 'To'
error # 1014 缺少 'End'
error # 1015 缺少 'Function'
error # 1016 缺少 'Sub'
error # 1017 缺少 'Then'
error # 1018 缺少 'Wend'
error # 1019 缺少 'Loop'
error # 1020 缺少 'Next'
error # 1021 缺少 'Case'
error # 1022 缺少 'Select'
error # 1023 缺少表达式
error # 1024 缺少语句
error # 1025 语句未结束
error # 1026 缺少整型常数
error # 1027 缺少 'While' 或 'Until'
error # 1028 缺少 'While' 和 'Until'或语句未结束
error # 1029 缺少 'With'
error # 1030 标识符过长
error # 1031 无效数字
error # 1032 无效字符
error # 1033 未结束的字符串常量
error # 1034 注释未结束
error # 1037 无效使用 'Me' 关键字
error # 1038 'loop' 语句缺少 'do'
error # 1039 无效的 'exit' 语句
error # 1040 循环控制变量 'for' 无效
error # 1041 名称重定义
error # 1042 必须是行中的第一个语句
error # 1043 不能为 non-ByVal 参数赋值
error # 1044 调用子程序时不能使用括号
error # 1045 缺少文字常数
error # 1046 缺少 'In'
error # 1047 缺少 'Class'
error # 1048 必须在一个类的内部定义
error # 1049 在属性声明中缺少 Let , Set 或 Get
error # 1050 缺少 'Property'
error # 1051 在所有属性的规范中,变量的数目必须一致
error # 1052 在一个类中不允许有多个缺省的属性/方法
error # 1053 类的初始化或终止程序没有变量
error # 1054 属性的 set 或 let 必须至少有一个变量
error # 1055 错误的 'Next'
error # 1056 'Default' 只能在 'Property' , 'Function' 或 'Sub' 中指定
error # 1057 指定 'Default' 时必须同时指定 'Public'
error # 1058 只能在 Property Get 中指定 'Default'
error # 4096 Microsoft VBScript 编译器错误
error # 4097 Microsoft VBScript 运行时错误
error # 5016 缺少正则表达式对象
error # 5017 正则表达式语法错误
error # 5018 错误的数量词
error # 5019 正则表达式中缺少 ']'
error # 5020 正则表达式中缺少 ')'
error # 5021 字符集越界
树形菜单:
<SCRIPT language="JavaScript">
var lastObj
function expandIt(obj)
{
if(lastObj != null)
{
if(obj == lastObj)
{
if(obj.style.display == "none")
{
obj.style.display = "";
}
else
{
obj.style.display = "none"
}
}
else
{
lastObj.style.display = "none";
obj.style.display = "";
}
}
else
{
obj.style.display = "";
}
lastObj = obj
}
</SCRIPT>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<%
dim id
id = request("id")
dim strsql,rs
strsql="select * from p_type where slanguage=1 and typelevel=1 order by typename"
set rs=fgetrslist(strsql)
do while not rs.eof
%>
<tr>
<td height="25" class="LEFTLINKS"><img width="30" height="0" align="absmiddle" /><b><%if rs("isleaf")=0 then%><a href="#" id")%>);return false"><%else%><a href="<%=request.ServerVariables("SCRIPT_NAME")%>?idtree=<%=rs("idtree")%>" ><%end if%><%=server.HTMLEncode(right((rs("typename")&""),len(rs("typename")&"")-2))%></a></b></td>
</tr>
<%
IF clng(id)=clng(rs("id")) then
%>
<tr id="kb<%=rs("id")%>">
<%
else
%>
<tr id="kb<%=rs("id")%>" style="display:none;">
<%
end if
%>
<td>
<table>
<%
dim rs1
strsql="select * from p_type where parentid="&rs("id")&" and slanguage=1 order by typename"
set rs1=fgetrslist(strsql)
do while not rs1.eof
%>
<tr><td height="20" class="LEFTLINKS">
<img width="40" height="0" align="absmiddle" /><a href="<%=request.ServerVariables("SCRIPT_NAME")%>?idtree=<%=rs1("idtree")%>&id=<%=rs("id")%>" ><%=server.HTMLEncode(right((rs1("typename")&""),len(rs1("typename")&"")-2))%></a></td></tr>
<%
rs1.movenext
loop
rs1.close
set rs1=nothing
%>
</table>
</td>
</tr>
<%
rs.movenext
loop
rs.close
set rs=nothing
%>
</table>

第七课.ASP中实现分页显示的七种武器

在微软的ASP编程体系中,ADO对象的建立,使得从网页访问数据库成为一件易事,特别是ADO的Recordset对象使得控制数据的输出显示更为方便、自由。而在Visual InterDev6.0(以下简称VI6.0)中,由于Script Object Model(以下简称SOM)、Design-Time Control(以下简称DTC)以及Data Environment Object Model(以下简称DEOM)等对象模型的引入,使网页对数据库的访问设计显得更为方便。
   因为主题方面的原因,关于数据库的连接,下文只给出代码和简要注释,而把重点放在如何利用Recordset对象(或控件)实现数据记录的分页显示方面。根据我的理解,分页显示的关键就在于对ADO的Recordset对象或DTC(设计时控件)的Recordset控件的属性和方法的熟练把握上。
   这七种分页显示的武器概括起来说分四类:
   第一、二种我暂取名叫“纯ASP法”,这也是国内的ASP网站上用得最多的方法,它们的区别仅在实现技巧的不同。这两种方法的实现最易理解,用到的对象概念也最少,对开发环境的要求也最低(只要记事本就行)。可以说,这两种方法的实质还是CGI的编程思想,只是在程序中引入了ADO对象而已。
   第四、五种暂取名叫“SOM的DHTML法”。这两种方法要求在VI6.0的环境下,利用微软提出的脚本对象模型(Script Object Model)和DHTML中Table对象的与数据库绑定的新特性(许多书和文章只介绍了DHTML的CSS特性在样式设计中的运用而忽略介绍其数据绑定特性),实现在客户端控制翻页。但它要求用户的浏览器必须是支持DHTML,如:Microsoft Internet Explorer 4.0及以上的版本。
   第六种暂取名叫“SOM服务器端法”。要求在VI6.0的环境下开发,它利用微软提出的脚本对象模型(Script Object Model)中的几个DTC控件:Recordset、PageObject、Grid等在服务器端(客户端)实现翻页控制。这是一种激动人心的、全新的编程方法,它把网页看成对象(这种对象模型和传统的DOM----document object model是有区别的:DOM只能控制客户端,而SOM可控制服务器端和客户端),它真正实现了网页的面向对象编程。但遗憾的是,也许是我个人能力有限,这种技术我个人认为还不是很成熟,比如,与浏览器的结合还不是很好,这将在后文详细说明。
   第七种暂取名叫“DEOM法”。它也是利用了VI6.0中建立的数据环境对象模型(Data Environment Object Model)建立Recordset对象。这也是在网页编程上比较少见的新方法,与SOM模型相比,自有它的优点,这将在后文详述。
   在后面所举的所有例子源代码,都可以直接拷贝使用,你甚至可以不懂其原理,只要把其中的粗斜体字部分换成相应自己的数据库名或字段名就可以了。
   在开始详细介绍各种分页方法前,让我们先创建一个数据库:用Office97中的access自创一个Employee.mdb,其中建一个表emp,只设三个字段:emp ID,last name和first name。为什么这么简单,是因为我们关心的是怎样处理recordset的结果。
   第一种:参数直接代入法
   这种方法是用手工建立Recordset对象,利用其pagesize(每页指定显示记录数),pagecount(总页码数)和absolutepage(当前页码数)属性来控制分页的输出。分页采用<href>直接带页码参数的方法来控制翻页。网页的名字为emp1.asp。源代码如下:
<%//建立与employee.mdb数据库的连接。
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open "driver={Microsoft Access Driver (*.mdb)};dbq=employee.mdb"
//建立emp表的Recordset对象实例rs。
Set rs = Server.CreateObject("ADODB.Recordset")
rs.Open "emp", conn, 3
PageSize = 10 //pagesize属性指定了每页要显示的记录条数
Page = CLng(Request("Page")) ’string型转化为long型
If Page < 1 Then Page = 1
If Page > rs.PageCount Then Page = rs.PageCount
If Page <> 1 Then
  Response.Write "<A HREF=emp1.asp?Page=1>第一页</A>"
  Response.Write "<A HREF=emp1.asp?Page=" & (Page-1) & ">上一页</A>"
End If
If Page <> rs.PageCount Then
  Response.Write "<A HREF=emp1.asp?Page=" & (Page+1) & ">下一页</A>"
  Response.Write "<A HREF=emp1.asp?Page="&rs.PageCount & ">最后一页</A>"
End If
Response.write"页码:" & Page & "/" & rs.PageCount & "</font>"
//每一页的显示
//显示表头
Response.Write "<CENTER><TABLE BORDER=1>"
Response.WRITE "<TR><TD>" & rs.Fields("emp ID").Name & "</TD>"
Response.WRITE "<TD>" & rs.Fields("last name").Name & "</TD>"
Response.WRITE "<TD>" & rs.Fields("first name").Name & "</TD></TR>"
//循环显示每条记录
rs.AbsolutePage = Page //把页码赋给absolutepage属性从而知当前页的首条记录号
For iPage = 1 To rs.PageSize //
Response.WRITE "<TR><TD>" & rs.Fields("emp ID").Value & "</TD>"
Response.WRITE "<TD>" & rs.Fields("first name").Value & "</TD>"
Response.WRITE "<TD>" & rs.Fields("last name").Value & "</TD></TR>"
rs.MoveNext
If rs.EOF Then Exit For
Next
Response.Write "</TABLE></CENTER>"%>
   第二种:表单传送参数法
   这种方法在创建Recordset对象时与第一种相同,只是在翻页控制时,采用<input>和case语句配合来实现翻页。网页的名字为:emp2.asp。此方法在编程逻辑上有个缺点:就是在按过“上页”或“下页”钮后,再在浏览器上按刷新按钮时,会自动翻页。源代码如下:
if Pagenum = "" Then Pagenum = 1 //从第一页开始显示
//建立数据库连接和Recordset对象实例rs。
与第一种方法相同,此处略过。
RS.Pagesize = 10 ’ 设置一页中显示的记录条数为10条
// 确定翻页的动作
Select Case Request("NAV")
Case ""
  session("Pagenum") = 1
case "First" ’ First Record
  session("Pagenum") = 1
case "Prev" ’ Previous Record
  if session("Pagenum") > 1 then
   session("Pagenum") = session("Pagenum") - 1
  End If
case "Next" ’ Next Record
  if session("Pagenum")< RS.PageCount then
   session("Pagenum") = session("Pagenum") + 1
  End if
case "Last" ’ Last Record
  session("Pagenum") = RS.PageCount
End Select
RS.Absolutepage = Clng(session("Pagenum")) //确定当前页的第一条记录号
// 显示当前页
同第一种方法,此处略过。
// Nav 翻页按钮设置
<form method="GET" action="emp2.asp">
<input type="submit" name="NAV" Value="首页">
<input type="submit" value="上页" name="NAV">
<input type="submit" value="下页" name="NAV">
<input type="submit" value="末页" name="NAV"></form>
   第三种:用Grid控件设计分页
   所有的方法中,这种方法最容易。你只需拖DTC中的Recordset控件和Grid控件到asp网页中就行了。而且,你还能选择是在服务器平台还是在客户端平台控制翻页。缺点就是你必须用它给定的格式显示,而不能自己自由控制表格的显示格式。
   方法如下:
   在VI6.0中建一个工程emp.vip。再在工程中添加一个asp网页:emp3.asp。
   第一步:选VI6.0菜单条上的“add data connect…”,按开发工具的导航提示,你就可以很容易地建立与Employee.mdb数据库的连接。从DTC工具栏中拖一个Recordset控件到网页中,并设置其属性。具体如图:
   当你拖控件到网页中时,VI6.0会自动提示你“是否使用Scripting object model”,按yes。
   第三步:从DTC工具栏中拖一个Grid控件到网页中,然后单击鼠标右键,设置其属性,如:选在第二步中创建的Recordset控件名,选择emp表中的字段,每页显示多少条记录以及显示格式等。非常简单方便,只要照着导航提示做就行了。
   第四种:DHTML法一。
   数据记录显示在一个HTML表中。它利用DHTML中表的数据绑定特性来控制记录的分页显示。缺点就是你的翻页方法将被限制为一种特定的方式:只能“上页”和“下页”而不能“首页”和“末页”。由于是在客户端控制翻页,所以,这种和第五种方法是速度最快的,但遗憾的是它只能在支持DHTML的浏览器上使用。
   在DHTML中,<TABLE>的DATASRC属性可使表格绑定到一个数据源,另一个属性DATAPAGESIZE可指定一页一次显示的记录数。
   我们来看下面的例子:
   第一步:拖Recordset控件到新建的网页emp4.htm中,设置其属性,方法同第三种,此处略。
   第二步:输入下面的代码:
<TABLE ID="Table1" DATASRC="#Recordset1_RDS" DATAPAGESIZE=5> //假定前面设定Recordset控件名为Recordset1。每页显示5条记录。
<THEAD>
<TH ALIGN="left" WIDTH=150>Emp ID</TH> //输出表头
<TH ALIGN="left" WIDTH=200>Last Name</TH>
<TH ALIGN="left" WIDTH=200>First Name</TH>
</THEAD>
<TR>
<TD><DIV DATAFLD="Emp ID"></DIV></TD> //输出表内容
<TD><DIV DATAFLD="Last Name"></DIV></TD>
<TD><DIV DATAFLD="First Name"></DIV></TD>
</TR>
</TABLE>
   第三步:然后,增加一对DTCs Button按钮控件来做翻页导航,一个命名为“btnPrevious”(上一页),一个命名为“btnNext”(下一页)。它们相应的脚本如下:
<SCRIPT LANGUAGE=VBScript>
Function btnPrevious_onclick()
Table1.previousPage()
End Function
Function btnNext_onclick()
Table1.nextPage()
End Function
</SCRIPT>
这种方法是对第四种方法的完善。采用手工编写脚本的方法,使我们能做“首页”,“末页”翻页导航按钮,并能确定每条记录的位置(记录号)。由于篇幅的关系,我在下面只介绍一个具体例子,并给出简要说明。其它关于DHTML和Recordset控件的一些属性和方法请读者自行参照相关书籍。这里需要提请注意的是,Recordset控件与第一、二种方法中介绍的ADO的Recordset对象有些不同:Recordset控件没有直接给出pagesize和pagecount等属性,需要用下面介绍的方法来计算。
   第一步:拖Recordset控件到新建的网页emp5.htm中,名字为Recordset1,设置其属性,方法同第三种,此处略。
   第二步:定义三个全局变量和编写Recordset1的ondatasetcomplete(数据设置完成时)脚本。
Dim gCurrentPageNumber //当前页号
Dim gMaxPageNumber //最大页数
Dim gRecordsPerPage //每页显示记录数
gRecordsPerPage = 5 // 设置每页显示记录数为5条记录。
Function Recordset1_ondatasetcomplete()
totalRecordCount = Recordset1.getCount() //总的记录条数
gMaxPageNumber = Int(totalRecordCount / gRecordsPerPage) //获得最大页数
If (totalRecordCount Mod gRecordsPerPage) > 0 then
gMaxPageNumber = gMaxPageNumber + 1
End If
End Function
   第三步:创建翻页导航按钮。
Function btnFirst_onclick() ’ 翻到首页
  gCurrentPageNumber = 1
  DisplayData()
End Function
Function btnPrevious_onclick() ’ 翻到上一页
  if gCurrentPageNumber > 1 Then
   gCurrentPageNumber = gCurrentPageNumber - 1
   DisplayData()
  End If
End Function
Function btnNext_onclick() ’ 翻到下一页
  if gCurrentPageNumber < gMaxPageNumber Then
   gCurrentPageNumber = gCurrentPageNumber + 1
   DisplayData()
  End If
End Function
Function btnLast_onclick() ’翻到末页
  gCurrentPageNumber = gMaxPageNumber
  DisplayData()
End Function
   第四步:编写显示每一页的函数。其中使用了许多DHTML的属性和方法,请读者自行参考相关书籍。
Sub DisplayData()
startRecord = ((gCurrentPageNumber - 1) * gRecordsPerPage) + 1 //计算每一页开始显示的记录号数(位置,第几条)
rowCtr = 1
lblPageNumber.innerHTML = gCurrentPageNumber & "/" & gMaxPageNumber
For recordPtr = startRecord To (startRecord + gRecordsPerPage - 1) //循环显示一页的各条记录
  If recordPtr > Recordset1.getCount() Then //显示空表
   Table1.rows(rowCtr).cells(0).innerHTML = "<P> </P>"
   Table1.rows(rowCtr).cells(1).innerHTML = "<P> </P>"
   Table1.rows(rowCtr).cells(2).innerHTML = "<P> </P>"
   Table1.rows(rowCtr).cells(3).innerHTML = "<P> </P>"
  Else //具体显示每一页
   Recordset1.moveAbsolute(recordPtr) //移动记录指针。
   empID = Recordset1.fields.getValue("emp ID")
   empLName = Recordset1.fields.getValue("first name")
   empFName = Recordset1.fields.getValue("last name")
   Table1.rows(rowCtr).cells(0).innerText = recordPtr ’ Counter
   Table1.rows(rowCtr).cells(1).innerText = empID
   Table1.rows(rowCtr).cells(2).innerText = empLName
   Table1.rows(rowCtr).cells(3).innerText = empFName
  End If
  rowCtr = rowCtr + 1
Next
End Sub
   另外,我们还需要在window对象的onload事件中编写如下脚本:
For rowCtr = 1 to gRecordsPerPage
  Table1.insertRow(rowCtr) ’ 插一新列
  For cellCtr = 0 to 3
   Table1.rows(rowCtr).insertCell()
  Next
Next
   第六种:服务器端控制翻页方法。
   如果我们在服务器端对数据进行分页形成HTML语句后再输出到客户端,就不会存在浏览器不支持DHTML的问题了。可是用服务器端法使得我们每次翻页时,都得让Recordset控件重新产生一次,因此速度肯定要比用DHTML的方法慢。但如果服务器足够快的话,这点慢客户是察觉不到的。
   下面的例子中,我将介绍一个新的DTC控件:PageObject。这个控件使被指定的网页成为一个网页对象,用户在此网页的服务器脚本中组织的子程序和函数可被看作是该网页对象的方法。它提供了管理状态信息的一种先进的方法:网页对象有一些属性(变量),用户可以定义这些属性的生存期。因为以上这些特性,使我们在编制翻页的脚本时非常方便。
   但这种方法的缺点是:当你按了“上页”或“下页”按钮后,再浏览器上的按刷新按钮,网页会自动翻页。另外,如果按了浏览器上的“回退”按钮后,再按翻页按钮,可能会出现一次乱翻。这都是因为网页对象属性(全局变量)造成的。
   第一步:拖Recordset控件到新建的网页emp6.asp中,名字为Recordset1,设置其属性,方法同第三种,此处略。
   第二步:拖PageObject控件到网页中,取名叫emplist。然后右键单击此控件打开属性页并设置MaxPageNumber,RecordsPerPage,CurrrentPageNumber三个属性(全局变量)。VI6.0可用get和set方法来读写它们的值,具体用法请查阅相关资料。
   第三步:编写Recordset1的ondatasetcomplete事件。
Function Recordset1_ondatasetcomplete()
recordsPerPage = 5
empList.setRecordsPerPage(recordsPerPage)//设置网页对象每页记录条数属性为5
totalRecordCount = Recordset1.getCount()//获得记录集的总条数
mpn = Int(totalRecordCount / recordsPerPage) //计算出mpn为总页数
If (totalRecordCount Mod recordsPerPage) > 0 then
mpn = mpn + 1
End If
empList.setMaxPageNumber(mpn)
End Function
   第四步:拖四个button控件到网页中,编写翻页控制脚本。我们主要是通过改变网页对象的CurrentPageNumber属性的值来实现翻页。
Function btnFirst_onclick()’ 翻到首页
  empList.setCurrentPageNumber(1)
End Function
Function btnPrevious_onclick()’ 翻到上一页
  cpn = empList.getCurrentPageNumber()
  if cpn > 1 Then
   empList.setCurrentPageNumber(cpn - 1)
  End If
End Function
Function btnNext_onclick()’ 翻到下一页
  cpn = empList.getCurrentPageNumber()
  if cpn < empList.getMaxPageNumber() then
   empList.setCurrentPageNumber(cpn + 1)
  End If
End Function
Function btnLast_onclick() ’ 翻到末页
  empList.setCurrentPageNumber( empList.getMaxPageNumber() )
End Function
   为保证首次进入该页时,显示的是第一页,我们还得编写该网页对象的onEnter事件。
Function empList_onEnter()
  If empList.firstEntered Then
   empList.setCurrentPageNumber(1)
  End If
End Function
   第五步:编写显示每一页的脚本。
<HR><TABLE BORDER=0><TR>//显示表头
<TH ALIGN="left" WIDTH=35></TH>
<TH ALIGN="left" WIDTH=150>Emp ID</TH>
<TH ALIGN="left" WIDTH=200>Last Name</TH>
<TH ALIGN="left" WIDTH=200>First Name</TH></TR>
<%
pageNumber = empList.getCurrentPageNumber()//计算翻页所需的各种参数,同DHTML法二
recordsPerPage = empList.getRecordsPerPage()
startRecord = ((pageNumber - 1) * recordsPerPage) + 1
lastRecord = Recordset1.getCount()
For recordPtr = startRecord To (startRecord + recordsPerPage - 1)%>
<%If Recordset1.EOF = True Then%>
<TR>
<TD> </TD>
<TD> </TD>
<TD> </TD>
<TD> </TD>
</TR>
<%Else%>
<%Recordset1.moveAbsolute(recordPtr)%>
<TR>
<% If recordPtr <= lastRecord Then %>
<TD><%=recordptr%></TD>
<%Else%>
<TD> </TD>
<% End If %>
<TD><%=Recordset1.fields.getValue("emp ID")%></TD>
<TD><%=Recordset1.fields.getValue("last name")%></TD>
<TD><%=Recordset1.fields.getValue("first name")%></TD>
</TR>
<%End If%>
<%Next%>
</TABLE><HR>
   第七种:Data Environment Object Model(数据环境对象模型)法
   Data Environment对象模型把ADO对象模型及它的对象----“Connection”,“Command”,“Recordset”,“Field”以及“Parameter”对象----抽象到一个更加容易的表单中。Data Environment Object Model把命令显露为方法。用户可以调用这些方法,这些方法会执行这些命令并返回所得到的记录集。关于DEOM对象模型详细资料请参考相关书籍。我们来看下面网页emp7.asp的例子:
   第一步:在VI6.0的“project Explorer”窗口中的工程项目上右击鼠标并从弹出式菜单选择“Add Data Connection”。根据VI给出的导航提示建立一个到数据库的连接之后,用户就添加了一个实现从ASP应用程序访问数据库的数据命令。同时,你将会在“Project Explorer”窗口中的global.asa文件下方看到一个“Data Environment”对象。
   第二步:右击“Data Environment”对象然后从弹出式菜单中选择“Add Data Command”选项,添加一个数据命令Command1。根据VI6.0的导航提示,你可以在Command1 Properties弹出窗口的Genetal页中选SQL Statement,输入:select * from emp。按OK返回。
   第三步:你创建了这个数据命令后,就已经为该Data Environment对象创建了一个方法,然后就可以从脚本中调用这个方法,而且该方法将会给用户返回一个记录集。
thisPage.createDE() //在SOM模式下,thisPage表示当前网页对象,createDE()方法创建了DE对象。
DE.Command1//执行DE对象的命令,后面可代参数,做有条件查询时很有用。
Set rs=DE.rsCommand1//DE.rscommand1使得rs对象完全等同于一个ADO的Recordset对象。
   第四步:因为rs为ADO对象,所以,以下的实现翻页代码完全参照以上介绍的几种方法,此处略过。
   其它还有如FrontPage2000的数据库导航中实现的方法等,因与本主题无关,此处略。
   综上所述,前面介绍的每种方法都包含了很多新的技术,由于篇幅的关系,无法深入。本文只是想通过实现翻页这一具体的例子来介绍ASP网页编程的多种方法;让大家亲身体验一下VI6.0在编制网页中的强大功能;了解和熟悉微软在网页编程中提出的ADO、DHTML、DTC控件、SOM对象模型和DEOM对象模型的使用方法;希望能给大家在编制网页时提供更多的选择和参考。


第八课.Web程序中网页间数据传递方法小结

介绍
我们总是会遇到这样的情况,需要将数值从一个网页传递到另一个网页。在这篇文章中,向你展示了几种从一个网页向另一个网页传递数值的几种方法。在此例子中,创建的网页由一个文本控件和几个按钮控件组成。在文本框中输入的数据通过被标识在按钮控件中的不同方法从一个网页传递到另一个网页。
Response.Redirect
让我们首先看一看如何使用Response.Redirect方法传递数据。这是它们之中最简单的方法。在文本框中输入一些数据,并且当你输入完成数据后,按下“Respose.Redirect”按钮。我们会得到一个提示,有时我们想在catch程序中传递另一个网页,意味着捕捉到例外程序并且向另一个网页传递。如果你试图这样做,它会给你一个System.Threading例外程序。因为你想遗留下一个线程向另一个网页传递数据,所以这个例外程序就会被抛出。
Response.Redirect("WebForm5.aspx",false);
这个语句告诉编译器定位到“WebForm5.aspx”,这里的“false”意味着在当前网页不能结束你正在做的事情。应该看一看线程发布命令的System.Threading类。在下面,看一看按钮事件的C#代码。“txtName”文本控件的名字,文本框的内的值传递到一个叫做“WebForm5.aspx”的网页。在“?”之后的“Name”符号只是一个临时的响应变量,这个变量保持着文本的数值。
private void Button1_Click(object sender, System.EventArgs e)
{
// Value sent using HttpResponse
Response.Redirect("WebForm5.aspx?Name="+txtName.Text);
}
好的,到这种观点为止,你使用Response发送了数值。刚刚,在此我收集到了这些数值,所以在“WebForm5.aspx”page_Load事件中,写入这些代码。首先,我们检查到输入的值不为null。如果不是这样,我们只是简单地在网页上使用Label控件显示数值。注意:如果你使用Response.Redirect方法来传递这些数值,所有这些数值在浏览器的URL中都是不可见的。你绝不能使用Response.Redirect来传递信用证号码和机密信息。
if (Request.QueryString["Name"]!= null)
Label3.Text = Request.QueryString["Name"];
Cookies
接下来使用Cookies。Cookies在服务器端创建,但是客户端省略。在此 “Cookies” 按钮的click事件中,写入以下代码:
HttpCookie cName = new HttpCookie("Name");
cName.Value = txtName.Text;
Response.Cookies.Add(cName);
Response.Redirect("WebForm5.aspx");
首先,创建一个cookie命名为“cName”。既然一个cookie实例可以拥有许多数值,告诉编译器这个cookie持有“Name”数值。我们将它赋值给TextBox并且最结后将它加入Response流,再使用Response.Redirect方法传递给其它网页。
让我们看一看如何得到被另一个网页传递的cookie数值。
if (Request.Cookies["Name"] != null )
Label3.Text = Request.Cookies["Name"].Value;
如你所看到的,象我们以前做一的一样正是使用同一种方法,刚刚我们在Request.QueryString之内,使用了Request.Cookies。记注一些浏览器不接收cookies。
Session Variables
接下来我们看一看session变量,这些变量由服务器来处理。第一个响影一从客户端传递到服务器,Sessions就创建了,并且当用户关闭浏览器窗口或者一些异常操作发生,session就会结束。给你一些可以使用session变量来传递数值的方法。在下面你看到为用户创建的Session和 “Name”是关键字,也如知名的Session关键字一样,关键字被赋给TextBox数值。
// Session Created
Session["Name"] = txtName.Text;
Response.Redirect("WebForm5.aspx");
// The code below shows how to get the session value.
// This code must be placed in other page.
if(Session["Name"] != null)
Label3.Text = Session["Name"].ToString();
Application Variables
有时,我们需要存取来自网页任何地方的数值。因为那样,可以使用Application变量。这里有一小段代码,这段代码显示如何做到那些事情。一旦创建Application变量并且为其赋值,在应用程序的任何地方都可以重新得到它的值。
// This sets the value of the Application Variable
Application["Name"] = txtName.Text;
Response.Redirect("WebForm5.aspx");
// This is how we retrieve the value of the Application Variable
if( Application["Name"] != null )
Label3.Text = Application["Name"].ToString();
HttpContext
可以使用HttpContext从网页中重新得到数值。通过使用方法的属性获得那些数值。既然它们易于编写代码和修改,使用属性是一种好方法。在你的第一个网页中,制造一个属性,这个属性可以返回TextBox的值。
public string GetName
{
get { return txtName.Text; }
}
我们使用Server.Transfer来将此控件发送到一个新网页。注意:Server.Transfer仅仅将此控件传递到新的网页并且不重新定位该网页,这意味着你会看到在URL中旧网页的地址。简单地在“Server.Transfer”按钮单击事件,并且增加下列代码。
Server.Transfer("WebForm5.aspx");
现在,让我们定位网页,数值就传递到该网页上,在这种情况下使用的该网页是“webForm5.aspx”。
// You can declare this Globally or in any event you like
WebForm4 w;
// Gets the Page.Context which is Associated with this page
w = (WebForm4)Context.Handler;
// Assign the Label control with the property "GetName" which returns string
Label3.Text = w.GetName;
Special Note
特别注意 与你看到的一样,从一个网页向别一网页传递数值时有不同的方法。每一个方法有它自己的优点也有其缺点。所以,当你传递数值时,选择好你所需要的所以你就会有一种好方法,这种方法对你是最为可行的。


第九课.如何轻松打造ASP计数器

以下介绍用数据库实现简单计数器
'下面存为count.asp
<%
Set conn=Server.CreateObject("ADODB.Connection")
conn.Open "driver={Microsoft Access Driver (*.mdb)};dbq="& Server.MapPath("count.mdb")
%>
<%on error resume next%>
<%sql="update count set hit=hit+1%><%conn.Execute(sql)%>
<%sql = "select * from count
set rs=conn.execute(sql)
%>
<%
'更新每周每日数据
lasthit=rs("lasthit")
tdate=year(Now()) & "-" & month(Now()) & "-" & day(Now())
if trim(lasthit)=trim(tdate) then
sql="update site set dayhit=dayhit+1 where id="&request("id")
conn.Execute(sql)
' response.write "success"
else
sql="update site set dayhit=1 where id="&request("id")
conn.Execute(sql)
' response.write "error"
end if
sql="update site set lasthit='"&tdate&"' where ID="&request("id")
conn.Execute(sql)

p_year=CInt(year(Now()))-CInt(year(lasthit))
p_month=CInt(month(Now()))-CInt(month(lasthit))
p_day=CInt(day(Now()))-CInt(day(lasthit))
period_time=((p_year*12+p_month)*30+p_day)
if cint(period_time)=<cint(7) then
sql="update site set weekhit=weekhit+1 where id="&request("id")
conn.Execute(sql)
else
sql="update site set weekhit=1 where id="&request("id")
conn.Execute(sql)
end if
%>
document.write('<tr><td width="100%">今日访问<%=rs("dayhit")%>次,本周访问<%=rs("weekhit")%>次,总访问<%=rs("hit")%>次</td></tr>');
<%rs.close
set rs=nothing%>
'用<script language="JavaScript1.1" src="count.asp"></script>在要统计的页面调用即可.
建立数据库:建一个count的MDB库,再建一个表count,表中字段为
hit 数字型
dayhit 数字型
weekhit 数字型
lasthit 日期型


第十课.用ASP构建音乐服务器的方法

---- 音乐服务器(Music Server)是指一个提供音乐在线服务的服务器,它包括高端提供门户服务的网站、Web数据库和低端的操作平台、硬件设施。目前,在Internet和Intranet上有不少这样的站点,特别是在一些高速宽带的局域网中(如校园网),音乐服务器给上网的朋友提供了休闲娱乐的好去处,同时也给网站带来了较高的访问率。

----像其他站点一样,音乐服务器包括网站和硬件两个部分。硬件性能和服务器效率是成正比的,因而如何构建网站才是一个音乐服务器的关键。从目前来看,网站基本有两种类型:一种是运行在Unix / Linux环境下,采用Perl / C / Php / Java 等作CGI编程语言;一种是运行在Win NT Server 平台上,采用ASP / WinCGI 作后台语言。前者运行效率高,但结构复杂,比较适合大型站点;后者编程难度相对较低,而且采用ODBC驱动接口,数据库连接方便,特别适合作音乐服务器。
----一个基本的音乐服务器包括音乐在线欣赏、音乐排行榜、音乐主题检索和音乐下载等四个部分。下面将主要从技术角度来讨论如何实现以上功能。
音乐主题数据库的规划
----音乐主题数据库是Web站点存储所需音乐资料的仓库,它的规划在很大程度上影响了整个网站的结构和效率。数据库中可以单独建成一张表单,也可以以主键和外键的形式建成多张表单。本例中为说明上的便利,建成如下单表框架: (music.mdb)
音乐在线欣赏
----在线欣赏是指客户端利用播放器播放服务器端的音乐文件。其原理是当客户端向服务器提交音乐选单后,服务器生成相应的.m3u文件,并将该文件通过Http协议下行至客户端;客户端将被激发调用相应的播放器执行该文件,从而实现了音乐在线欣赏功能。目前支持.m3u文件的播放器有Winamp、Realplayer G2、 Musicmatch等。当这些播放器软件被正确安装在客户端时,就可以自动播放.m3u文件。所以解决问题的关键在于后台如何生成.m3u文件并下行到客户端。以下利用ASP中内置的FileSystem组件给出一种解决方案,并给出相应程序。
<%
dim choose,path,mydb,myset,SQL,fs,mp3
‘##### 获得list.htm表单中选中的歌曲项
对应的id号,并赋给字串变量choose #####
choose=“("
for i=3 to request.form.count
choose=choose+request.form(i)+“,"
next
choose=left(choose,len(choose)-1)+“)"
‘##### 判断choose变量,如果不包含任何id号,
说明list.htm中没有选中任何歌曲,终止程序#####
if choose=“()" then
response.redirect(“list.htm")
response.end
end if
‘#####设置文件路径,需要把temp目录的权限设为
对internet匿名用户具有read & write 权限 #####
path=“E:\inetpub\wwwroot\temp\"
‘##### 创建文件对象 #####
Set fs = CreateObject(“Scripting.FileSystemObject")
Set mp3 = fs.CreateTextFile(path+“listen.m3u", True)
‘##### 创建数据库对象#####
set mydb=server.createobject(“adodb.connection")
mydb.open “music"
‘##### 检索数据库,获得歌曲信息 #####
SQL=“select mp3name,url from "&dbname&
“where id in "&choose
set myset=tdb.execute(SQL)
do while not myset.eof
‘##### 生成点播歌曲文件列表 #####
mp3.Write(“http://"+myset(“url")+chr(10))
myset.movenext
loop
‘##### 更新数据库中的当天点播次数和
总共点播次数 #####
SQL=“update music set click=click+1,
this=this+1 where id in "&choose
mydb.execute(SQL)
‘##### 取消对象 #####
set myset=nothing
mydb.close
set mydb=nothing
mp3.close
set mp3=nothing
‘##### 将该文件下载给用户#####
response.redirect(“listen.m3u")
response.end
%>
----注意:利用这种方法时,要控制.m3u文件Http头的内容。在Winnt中可利用IIS设置.m3u文件类型的Mine内容。具体操作如下:启动IIS —> 选中音乐服务器所在的Web站点 —> 点击“属性” 按钮 —> 在出现的属性选项卡中点击“Http标题”卡 —> 点击“文件类型”按钮 —> 点击“新增类型”按钮 —> 在“相应的扩展名”中填入“.m3u”,在“内容类型”中填入“audio/mpegurl” —> 然后一路确定即可。
音乐排行榜
----音乐排行榜是音乐服务器不可缺少的一项内容,其重要性不亚于一个站点的Pageview。它可以向歌迷即时提供信息、引导欣赏、动态地反映潮流趋势。
在众多的音乐服务器中,排行榜也是竟相推陈出新的地方,具有很高的点击率。一般说来,排行榜包括总共点播次数、当天点播次数、总共下载次数等内容。具体的实现方法比较简单。在单表中可以利用如下SQL语句:select * from music order by total_click 。若是多表,则可以利用带join子句的联合查询SQL语句。如果要限定查询记录数,可采用count()集合函数。如在本例中,如果要查询当天点播次数前20名的歌曲,可采用如下语句:select top 20 * from music order by total_click desc 。排行榜的功能主要取决于主题数据库的规划如何,你可以根据需要增删字段,以实现相应功能(如增加进榜时间、歌手资料、名次变化等等),所涉及的SQL语句也不会过于复杂。总之,排行榜是体现一个音乐网站特色所在,可以自由发挥。
音乐主题检索
----提到检索,很多人立刻联想到Yahoo、Soho等著名站点,并且颇有神秘之感。其实,搜索引擎在数据库中并非难事,因为数据库内置的数据引擎已经提供了很好的基础。搜索效率的高低取决于数据库的性能和SQL语句的效率。在前台,可以提供一系列的检索项目和条件选项。在后台,则根据前台提交的表单,生成相应的查询语句在数据库中执行,并将查询结果返回。比如在前台提交的表单为:检索项目=“歌手姓名”,内容=“张学友”,匹配条件=“全字匹配”,则后台生成的SQL 语句为:select * from music where singer=‘张学友' order by edition,id asc,这样就可以检索出张学友的所有歌曲信息,并按专集分类返回。又如前台要查询的歌手姓名为“齐秦”,且歌曲名称带有“雨”字的所有歌曲(即要求模糊匹配),则后台生成的SQL语句为: select * from music where singer =‘齐秦' and mp3name like ‘%雨%' order by id asc ,将返回“冬雨”、“太阳雨”、“无情的雨无情的你”等等。只要运用适当的技巧和灵活的 SQL语句,就可以让你的主题检索发挥得淋漓尽致。
音乐下载功能
----提供音乐下载功能,同样是音乐服务器的一个基本功能,特别是对远程用户,将喜爱的歌曲下载后才能欣赏。
一般有两种方式提供下载,一种是直接通过Http和浏览器下载,另一种是将曲库开辟为Ftp目录,通过ftp协议下载。本例中采用前一种方式,并且在数据库中对下载次数进行跟踪记录。有些站点还根据需要对歌曲进行压缩加密,将密码提供给正式用户,也是一条可取之道。本例中程序如下:
<%
‘##### 获得歌曲标志号id #####
id=request(“id")
set tdb=server.createobject(“adodb.connection")
tdb.open “music"
SQL=“select mp3url from music where id ="&id
set tset=tdb.execute(SQL)
if tset.eof then
response.end
else
‘##### 更新数据库中歌曲的下载次数 #####
SQL=“update music set total_down=total_down
+1 where id ="&id
tdb.execute(SQL)
downfile=tset(“url")
tdb.close
set tset=nothing
set tdb=nothing
end if
if downfile=“" or isnull(downfile) then response.end
downfile=“http://"+downfile
‘##### 下载相应歌曲 #####
response.redirect(downfile)
response.end
%>
----以上步骤就构建了一个基本的音乐服务器。当然,一个完整的音乐服务器还可以包括歌手信息、歌迷论坛、聊天室、投票站、娱乐新闻网等等功能,都可以用ASP一一实现。本文限于篇幅,不再详细讨论。只要前台页面采用独特的风格设计和完善的JavaScript程序控制,后台编程运用灵活的SQL语句和强大的ASP组件,加之规划周到的Web数据库和丰富的创意,就能构建一个完美的音乐服务器。有兴趣的朋友不妨试试看,你会创造一个奇迹的!


第十一课.ASP中几种分页显示的比较

面通过对比来看看几种方式的用时对比。  

  一,使用存储过程分页,这种情况又分为两种方式:  

  第一种,使用command对象,如下:  


  Set Cmd=server.CreateObject("Adodb.Command")
Cmd.ActiveConnection=conn
  Cmd.CommandText="ycuu_gb_getmsg"
  Cmd.CommandType=4'adCmdStoredProc
  cmd.prepared=true'
  set param=Cmd.CreateParameter("@iPageNo",adInteger,1,2,Page)
  Cmd.Parameters.Append param
  set param=Cmd.CreateParameter("@iPageSize",adInteger,1,2,PageSizeConst)
  Cmd.Parameters.Append param
  set rs=Cmd.execute  
  第二种,使用connection对象的执行方法直接执行,具体如下:  
  set rs=conn.execute ("execute ycuu_gb_getmsg "&page&", "&pagesizeConst)  
  二,不使用存储过程,直接使用ADODB.RecordSet的功能来分页,具体代码如下:  
  Set rs = Server.CreateObject("ADODB.Recordset")
  sql = "Select * FROM Guestbook Order By dateandtime Desc"
  rs.open sql,conn,1,1
  rs.pagesize = 150'每页显示的留言数量,
  total = rs.RecordCount
  mypagesize = rs.pagesize
  rs.absolutepage = page 
  为了更加明显地显示出速度,我把每页显示的留言数量加大到150(事实上当然不会设置这么大的数值啦)。至于我机器的配置,就省略不说了,因为主要是速度对比。  
  发现,执行的时候时间分别如下: 
  第一种:稳定于0.1953125 秒到0.2109375 秒之间,平均值大概是:0.20秒  
  第二种:稳定于0.1716875 秒到0.1857秒之间,平均值大概是:0.177秒  
  第三种:稳定于0.4375 秒到0.4632秒之间,平均值大概是:0.45秒  
  但是,当读取的记录条数为20的时候,结果如下:
  发现,执行的时候时间分别如下:  
  第一种:稳定于.0390625 秒到.0546875 秒之间,平均值大概是:0.045秒  
  第二种:稳定于0.046875 秒到.0546875 秒之间,平均值大概是:0.050秒  
  第三种:稳定于.09375 秒到0.1015625 秒之间,平均值大概是:0.97秒  
  在这样看来,似乎conn.execute和command.execute这两种方式似乎差别并不大,
  而前者的调用方式好像更加简单一点。
  同时,在这里可以看出分页的存储过程速度确实比recordset的分页速度要快很多.

第十二课.ASP如何获取客户端真实IP地址

要想透过代理服务器取得客户端的真实IP地址,就要使用 Request.ServerVariables("HTTP_X_FORWARDED_FOR") 来读取。不过要注意的事,并不是每个代理服务器都能用 Request.ServerVariables("HTTP_X_FORWARDED_FOR") 来读取客户端的真实 IP,有些用此方法读取到的仍然是代理服务器的IP。还有一点需要注意的是:如果客户端没有通过代理服务器来访问,那么用 Request.ServerVariables ("HTTP_X_FORWARDED_FOR") 取到的值将是空的。因此,如果要在程序中使用此方法,可以这样处理:
......
  userip = Request.ServerVariables("HTTP_X_FORWARDED_FOR")
  If userip = "" Then userip = Request.ServerVariables("REMOTE_ADDR")
  ......   
  即:如果客户端通过代理服务器,则取 HTTP_X_FORWARDED_FOR 的值,如果没通过代理服务器,就取 REMOTE_ADDR 的值。   
  '通用函数:如果不能取客户端真实IP,就会取客户端的代理IP
  Private Function getIP()
  Dim strIPAddr
  If Request.ServerVariables("HTTP_X_FORWARDED_FOR") = "" OR InStr(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), "unknown") > 0 Then
  strIPAddr = Request.ServerVariables("REMOTE_ADDR")
  ElseIf InStr(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), ",") > 0 Then
  strIPAddr = Mid(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), 1, InStr(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), ",")-1)
  ElseIf InStr(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), ";") > 0 Then
  strIPAddr = Mid(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), 1, InStr(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), ";")-1)
  Else
  strIPAddr = Request.ServerVariables("HTTP_X_FORWARDED_FOR")
  End If
  getIP = Trim(Mid(strIPAddr, 1, 30))
  End Function


第十三课. 使用ASP建设私人的搜索引擎

很多网络爱好者在创建自己的个人主页时,都绞尽脑汁让自己网站的功能更全面。在此,笔者介绍一种使用ASP建立自己的搜索引擎的方法。

  基本思路 利用表单将用户提交的搜索关键字存储在变量中,并提交给ASP脚本处理。利用ASP内建的“REQUEST”对象获取变量中的关键字符,随后用“RESPONSE”的“REDIRECT”函数将关键字符转向提交到其他的诸如搜狐、网易等专业搜索引擎中,即可得出搜索结果。使得访客在自己的主页上就可方便地使用各大搜索引擎,无需登录其主页面。


第一步 创建搜索引擎的主页面。在HTML文件
的< body>和< /body>之间加入代码如下:
  < form name="form1" method="post" action="search.asp">
  < div align="center">请选择您喜欢的搜索引擎< br>< br>
  < select name="select" size="1">
  < option>搜狐< /option>
  < option>新浪(北京站)< /option>
  < option>网易< /option>
  < /select>< br>< br>
  请键入您要查询的关键字< br>
  < input type="text" name="textfield">
  < br>< br>
  < input type="submit" name="Submit" style="color:#CC0033;background-color:#ffffff;font-size:9pt;border:#CC0033 1px solid;height:18px"value="搜 索">
  < /form>
  在此,我们提供了搜狐、新浪、网易3种搜索引擎供用户选择,用户键入关键字串后,表单将请求提交到后台的 search.asp 处理。
  第二步 编写后台的ASP程序。在HTML文件的< body>和< /body>之间加入下列代码:
  
< % if request.form("select")="搜狐" then response.redirect("
http://site.search.sohu.com/

  sitesearch.jsp?key_word="&&request.form("textfield")) end if
  if request.form("select")="新浪(北京站)" then response.redirect("
http://site.search.sohu.com/

  sitesearch.jsp?key_word="&&request.form("textfield")) end if
  if request.form("select")="网易" then response.redirect("
[url='http://search.163.com/cgi-bin/search/engine/search2.fcgi?lang=gb&&key=]http://search.163.com/cgi-bin/search/engine/search2.fcgi?lang=gb&&key="&&request.form("textfield[/url]
")) end if
  %>
  在实现提交搜索字串到其他搜索引擎时,很关键的一点就是要明白这些搜索引擎所使用的查询格式。如搜狐用的就是“http://site.search.sohu.com/sitesearch.jsp?key_word=”搜索字串 。我们只需平时在使用这些搜索引擎时注意一下,给出搜索结果时把IE地址栏中的地址记录下来分析,去掉后面的诸如“%C1%F5%……”字符(这是我们提交的搜索字串转换成的字符),即可得到查询格式。
  另外,很多搜索引擎使用了分类查找。可细分为“网站”、“网页”、“新闻”等类别,每一种类的查询格式均不相同,读者可以使用更多的IF语句建立选择项,实现同一搜索引擎内搜索种类的细分。
  同理,我们还可把这个程序进行扩充,把其他的搜索引擎加入,使其功能更为强大。

第十四课.用ASP打开远端MDB文件的方法

如果你用ODBC connection (DSN or DSN-less)来访问远端的(UNC path)数据库, OLEDB会出现以下错误信息:  

  Microsoft OLE DB Provider for ODBC Drivers error ’80004005’   
[Microsoft][ODBC Microsoft Access Driver] The Microsoft Jet database engine cannot open the file ’(unknown)’. It is already opened exclusively by another user, or you need permission to view its data.  
  你完全可以避免这种错误--ASP和ActiveX支持两种方式打开MDB文件的DSN-less连接,或由其它机器访问MDB文件。  
  1. DAO database (only for small load)   
  Dim File, Conn, RS  
  Const ReadOnly = False  
  File = "
[url=file://server/share/file.mdb]\\server\share\file.mdb[/url]
"  
  Set Conn = CreateObject("DAO.DBEngine.35").Workspaces(0).OpenDatabase(File,,ReadOnly)
  Set RS = Conn.OpenRecordset(SQL)   
  2. ADO + Jet OLE DB provider  
  Dim Conn, RS  
  Set Conn = CreateObject("ADODB.Connection")  
  Conn.Provider = "Microsoft.Jet.OLEDB.4.0"  
  Conn.Open "
[url=file://server/share/file.mdb]\\server\share\file.mdb[/url]
"  
  Set RS = Conn.Execute(SQL)   
  你得确定使用ASP的用户有NT的数据库及共享访问权限。   
  假定有权限的话,你亦可访问其它机器中的开放数据连接:   
  
http://www.pstruh.cz/
 
  Set UM = CreateObject("UserManager.Server")   
  UM.LogonUser "Login with the rights", "Password", "Domain"
  ...  
  open database  
  ...   
  UM.RevertToSelf
posted on 2007-03-16 21:00 求勿求 阅读(3671) 评论(0)  编辑 收藏 引用 所属分类: IT歧途
只有注册用户登录后才能发表评论。