| 使用ASP、VB和XML建立运行于互联网上的应用程序 |
热 ★★★★★ |
| 使用ASP、VB和XML建立运行于互联网上的应用程序 |
|
| |
|
|
让我们先分析一下客户端/服务器应用程序。在一个标准的客户端/服务器应用程序中,在应用程序开始时,你能够初始化数据库连接字符串,这就意味着,客户有使用数据库连接字符串的权利,这包括用户名和口令。
但是客观情况如果不允许你在网络上发送这些信息的话,你就必需在不联接数据库的情况下直接从客户端取得数据发送给客户。那么解决方案之一就是在服务器上创建一个ASP页(在本例中称为getData.asp)接收特定格式的POST数据,它要求一个包含XML字符串,用来创建ADO对象并运行存储过程或动态SQL语句命令。
如果信息有效的话,getData.asp执行存储过程,并返回一个XML格式的数据集、返回值列表或错误页面信息的XML字符串。对于返回数据的命令,客户端要么重新实例化要么返回值或使用XML DOM(Document Object Model文档对象模型)格式的错误页面。
下面就让我们来讨论一下如何实现这个页面吧!
getData.asp页面首先使用一个DOMDocument对象来保存客户端发送的数据:
'创建DOMDocument对象
Set xml = Server.CreateObject ("msxml2.DOMDocument")
xml.async = False
然后,它装载POST数据
'装载POST数据
xml.Load Request
If xml.parseError.errorCode <> 0 Then
Call responseError ("不能装载XML信息。" & "Description: " & xml.parseError.reason & "<br>Line: " & xml.parseError.Line)
End If
它能够返回commandtext元素值和returndata或returnvalue元素值。下面我只给出返回commandtext元素值的代码,其余代码请参看我下面所附的源程序。
Set N = xml.selectSingleNode("command/commandtext")
If N Is Nothing Then
Call responseError ("缺少 <sp_name> 参数。")
Else sp_name = N.Text
End If
接着,应该让页面创建一个Command对象,读入所有<param>元素,并且为request中的每一个元素创建一个参数。最后,让页面打开一个连接使用存储过程adExecuteNoRecords选项来执行request。
set conn = Server.CreateObject("ADODB.Connection")
conn.Mode=adModeReadWrite
conn.open Application("ConnectionString")
set cm.ActiveConnection=conn
' 返回数据
if not returnsData then
cm.Execute
else
set R = server.CreateObject("ADODB.Recordset")
R.CursorLocation = adUseClient
R.Open cm, ,adOpenStatic, adLockReadOnly
end if
如果能够返回数据的话,那么returnData变量就为真值,并且把结果数据集返回到客户端,仍然是一个XML文档。
if returnsData then
R.Save Response, adPersistXML
if err.number <> 0 then
call responseError ("数据集发生存储错误" & "在命令'" & CommandText & "': " & Err.Description)
Response.end
end if
如果输出参数返回值,那么这个页面将返回一个包含这些值的XML字符串。文档的根元素是一个<values>标记,每一个返回值都有其相应的子元素,如果发生任何错误,页面都会格式化并返回一个包含错误信息的XML字符串:
Sub responseError(sDescription)
Response.Write "<response><data>错误: " & sDescription & "</data></response>"
Response.end
End Sub
假设在我们前面所说的例子中,我们想在应用程序中显示区域的左半边显示客户的姓名列表,再在每个客户姓名后面加上两个链接:Purchase History和Recent Purchase。当用户点击其中的一个链接,客户程序就会运行一个存储过程并在右边区域显示出结果。为了显示这个想法的灵活性,我想让用于返回数据的三个操作单元执行不同的工作过程,它们都调用getData.asp。
首先,通过调用CustOrderHist来运行一个存储过程,返回客户的Purchase History,它搜索Northwind数据库(为了方便起见我使用MS SQL中自带的数据库)并返回一个数据集。用于返回Recent Purchase 的查询语句运行一个叫RecentPurchaseByCustomerID的存储过程,来接收输入的CustomerID参数并通过ProductName参数返回最近顾客购买的商品名。定义其处理过程相应SQL语句如下:
CREATE PROCEDURE RecentPurchaseByCustomerID @CustomerID nchar(5), @ProductName nchar(40) output AS SELECT @ProductName = (SELECT top 1 ProductName FROM Products INNER JOIN ([Order Details] INNER JOIN Orders ON Orders.OrderID=[Order Details].OrderID) ON Products.ProductID = [Order Details].ProductID WHERE Orders.OrderDate = (SELECT MAX(orders.orderdate) FROM Orders
where CustomerID=@CustomerID) AND Orders.CustomerID=@CustomerID) GO
不管你的查询语句中含有动态SQL语句还是含有返回记录集的存储过程或是输出一个返回值,其处理POST消息的方法是一样的:
set xhttp = createObject ("msxml2.XMLHTTP")
xhttp.open "POST", "http://localhost/myWeb/ getData.asp", False
xhttp.send s
现在让我们看一看如何发送和接收数据
客户端的XML信息是由一个<command>元素和一些子元素组成:<commandtext>元素包含了存储过程的名称,<returnsdata>元素告诉服务器,客户端是否要求接收返回数据,<param>元素包含参数信息。如果不使用参数的话,那么最简单的发送字符串查询就象下面这样:
<command>
<commandtext>
存储过程或动态SQL语句
</commandtext>
<returnsvalues>True</returnsvalues>
</command>
你可以为每一个参数使用一个<param>元素,来添加参数。每个<param>元素有五个子元素:name,type,direction,size和value。子元素的顺序可以随意调换,但是所有的元素都应当有不能缺少,我通常按照定义一个ADO对象的值的顺序来定义它们。举例来说,CustOrderHist存储过程需要一个CustomID参数,所以用来创建发送到getData.asp的XML字符串的代码为:
dim s
s = "<?xml version=""1.0""?>" & vbcrlf
s = s & "<command><commandtext>"
s = s & "CustOrderHist"
s = s & "</commandtext>"
s = s & "<returnsdata>" &True</returnsdata>"
s = s & "<param>"
s = s & "<name>CustomerID</name>"
s = s & "<type><%=adVarChar%></type>"
s = s & "<direction>" & <%=adParamInput%></direction>"
s = s & "<size>" & len(CustomerID)& "</size>"
s = s & "<value>" & CustomerID &"</value>"
s = s & "</param>"
s = s & "</command>"
注意,前面的代码都是客户端代码,ADO常量是不在客户端定义的-这就是它们为什么使用<% %>标记围起来的原因。服务器在发送响应之前使用正确的值取代它们。getData.asp页有一个Response.ContentType,它的属性为"text/xml",这样,你就可以使用ResponseXML属性来返回结果了。当请求返回纪录,你就可以创建一个Recordset对象并且使用XMLHTTP来打开它:
Dim R
set R = createObject("ADODB.Recordset")
R.open xhttp.responseXML
当查询语句返回数据时,通过设置XMLHTTPRequest对象的responseXML属性来创建一个DOMDocument:
Dim xml
set xml = xhttp.responseXML
输出参数的XML字符串的每个返回值都包含一个元素,它们都是根元素<values>的直接子元素,例如:
<?xml version=""1.0"" encoding=""gb2312""?>
<values>
<paramname>value</paramname>
<paramname>value</paramname>
</values>
如果你的数据使用别的国家的文字,你可能需要把编码属性用相应的编码替换,例如对于大部分欧洲语言,可以使用ISO-8859-1
客户端页面使用返回的数据来格式化一个HTML字符串用于显示,如:
document.all("details").innerHTML = <一些格式化的HTML字符串>
前面我们已经介绍了使用ASP和XML混合编程,那是因为ASP页面能够很容易让我们看清应用程序正在做什么,但是你如果你不想使用ASP的话,你也可以使用任何你熟悉的技术去创建一个客户端程序。下面,我提供了一段VB代码,它的功能和ASP页面一样,也可以显示相同的数据,但是这个VB程序不会创建发送到服务器的XML字符串。它通过运行一个名叫Initialize的存储过程,从服务器取回XML字符串,来查询ClientCommands表的内容。
ClientCommands表包括两个域:command_name域和command_xml域。客户端程序需要三个特定的command_name域:getCustomerList,CustOrderHist和RecentPurchaseByCustomerID。每一个命令的command_xml域包括程序发送到getData.asp页面的XML字符串,这样,就可以集中控制XML字符串了,就象存储过程名字所表现的意思一样,在发送XML字符串到getData.asp之前,客户端程序使用XML DOM来设置存储过程的参数值。我提供的代码,包含了用于定义Initialize过程和用于创建ClientCommands表的SQL语句。
提供的例程中还说明了如何使用XHTTPRequest对象实现我在本文一开始时许下的承诺:任何远程的机器上的应用程序都可以访问getData.asp;当然,你也可以通过设置IIS和NTFS权限来限制访问ASP页面;你可以在服务器上而不是客户机上存储全局应用程序设置;你可以避免通过网络发送数据库用户名和密码所带来的隐患性。还有,在IE中,应用程序可以只显示需要的数据而不用刷新整个页面。
在实际的编程过程中,你们应当使用一些方法使应用程序更加有高效性。你可以把ASP中的关于取得数据的代码端搬到一个COM应用程序中去然后创建一个XSLT变换来显示返回的数据。好,我不多说了,现在你所要做的就是试一试吧!
Option Explicit
Private RCommands As Recordset
Private RCustomers As Recordset
Private RCust As Recordset
Private sCustListCommand As String
Private Const dataURL = "http://localhost/XHTTPRequest/getData.asp"
Private arrCustomerIDs() As String
Private Enum ActionEnum
VIEW_HISTORY = 0
VIEW_RECENT_PRODUCT = 1
End Enum
Private Sub dgCustomers_Click()
Dim CustomerID As String
CustomerID = RCustomers("CustomerID").Value
If CustomerID <> "" Then
If optAction(VIEW_HISTORY).Value Then
Call getCustomerDetail(CustomerID)
Else
Call getRecentProduct(CustomerID)
End If
End If
End Sub
Private Sub Form_Load()
Call initialize
Call getCustomerList
End Sub
Sub initialize()
' 从数据库返回命令名和相应的值
Dim sXML As String
Dim vRet As Variant
Dim F As Field
sXML = "<?xml version=""1.0""?>"
sXML = sXML & "<command><commandtext>Initialize</commandtext>"
sXML = sXML & "<returnsdata>True</returnsdata>"
sXML = sXML & "</command>"
Set RCommands = getRecordset(sXML)
Do While Not RCommands.EOF
For Each F In RCommands.Fields
Debug.Print F.Name & "=" & F.Value
Next
RCommands.MoveNext
Loop
End Sub
Function getCommandXML(command_name As String) As String
RCommands.MoveFirst
RCommands.Find "command_name='" & command_name & "'", , adSearchForward, 1
If RCommands.EOF Then
MsgBox "Cannot find any command associated with the name '" & command_name & "'."
Exit Function
Else
getCommandXML = RCommands("command_xml")
End If
End Function
Sub getRecentProduct(CustomerID As String)
Dim sXML As String
Dim xml As DOMDocument
Dim N As IXMLDOMNode
Dim productName As String
sXML = getCommandXML("RecentPurchaseByCustomerID")
Set xml = New DOMDocument
xml.loadXML sXML
Set N = xml.selectSingleNode("command/param[name='CustomerID']/value")
N.Text = CustomerID
Set xml = executeSPWithReturn(xml.xml)
productName = xml.selectSingleNode("values/ProductName").Text
' 显示text域
txtResult.Text = ""
Me.txtResult.Visible = True
dgResult.Visible = False
' 显示product名
txtResult.Text = "最近的产品是: " & productName
End Sub
Sub getCustomerList()
Dim sXML As String
Dim i As Integer
Dim s As String
sXML = getCommandXML("getCustomerList")
Set RCustomers = getRecordset(sXML)
Set dgCustomers.DataSource = RCustomers
End Sub
Sub getCustomerDetail(CustomerID As String)
' 找出列表中相关联的ID号
Dim sXML As String
Dim R As Recordset
Dim F As Field
Dim s As String
Dim N As IXMLDOMNode
Dim xml As DOMDocument
sXML = getCommandXML("CustOrderHist")
Set xml = New DOMDocument
xml.loadXML sXML
Set N = xml.selectSingleNode("command/param[name='CustomerID']/value")
N.Text = CustomerID
Set R = getRecordset(xml.xml)
' 隐藏 text , 因为它是一个记录集
txtResult.Visible = False
dgResult.Visible = True
Set dgResult.DataSource = R
End Sub
Function getRecordset(sXML As String) As Recordset
Dim R As Recordset
Dim xml As DOMDocument
Set xml = getData(sXML)
Debug.Print TypeName(xml)
On Error Resume Next
Set R = New Recordset
R.Open xml
If Err.Number <> 0 Then
MsgBox Err.Description
Exit Function
E [1] [2] 下一页
 |
频道声明:本频道的文章除部分特别声明禁止转载的专稿外,可以自由转载.但请务必注明出出处和原始作者 文章版权归本频道与文章作者所有.对于被频道转载文章的个人和网站,我们表示深深的谢意。
| 原始作者:佚名 |
录入时间:2007-1-7 17:59:34 |
| 信息来源:不详 |
投稿信箱:itqoo@126.com |
|
|
 |
|
|
|
| 教程录入:itqoo 责任编辑:itqoo |
|
上一个教程: VB.NET编程调用迅雷下载文件(1)
下一个教程: 用ASP、VB和XML建立互联网应用程序(1) |
| 【字体:小 大】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口】 |