发信人: fancynet()
整理人: winsy(2003-03-09 13:23:08), 站内信件
|
用VB和XML建立集中式应用程序(上)
(作者:青苹果工作室编译 2001年03月19日 15:41)
很长一段时间以来,Visual Basic程序员都是在编写客户机/服务器程序,这些程序工作在私有网络上。但是这种在一台PC机上安装一个独立应用程序的时代正在很快地结束,客户机/服务器结构不再能满足需要,现在的大多数商用程序需要共享数据。
为什么要建立基于Intranet和Internet的应用程序
编写基于Intranet和Internet的应用程序或修改C/S结构的程序为工作于Intranet和Internet上的程序的原因至少有以下几点:
首先,每天都在增加的远程雇员需要访问公司的数据。
其次,通过集中应用程序里的数据能监控对它们的访问和使用。
第三,使用本文谈及的技术,在应用程序启动时从一个中心位置查找全局设置信息,就能方便地维护和更新这些设置,有助于最大程度地降低对桌面应用程序的更新。
第四,通过Web服务器而不是从远程客户端来进行数据库操作,可以避免通过网络传递数据库的登录口令。
最后,如果使用Internet Explorer,通过后台查询数据,就能避免在修改部分内容时重画整个页面。
右图是在Internet Explorer中看到的一个应用程序实例。图中,应用程序显示了Northwind数据库中的信息。左边是客户名列表和两个链接:购买历史情况和最近的购买情况。当用户点击链接时,数据在右边的Details栏里显示出来。这个应用程序使用DHTML显示Details中的数据而不用刷新整个页面,而且没有使用框架。
实现思路
创建基于HTTP的VB应用程序的关键是XML和XMLHTTPRequest对象,XMLHTTPRequest对象是Microsoft的XML语法分析器 (msxml.dll)的一部分,它让你能通过HTTP向远端服务器发送GET和POST请求。运行在远端服务器上的程序(我们将要使用的是 ASP页面,但应用程序可以使用任何服务器端脚本机制)接受请求、解释内容,并向调用它的应用程序返回数据或错误消息。你可能会觉得这和对SOAP的描述差不多,实际上也是如此,但这里我们不使用SOAP,因为它会使代码变得复杂。无论如何,重要的是:要理解这里所用的技术背后的思想,它和SOAP的思想是一样的,但不像SOAP那样复杂。
将不停改变的客户端应用程序完全隔离起来是不可能的,但通过从中心服务器而不是从本地INI文件或Windows注册表中来加载应用程序设置,就能在很高程度上建立应用程序的独立性。例如,假定有一个流动的销售团队,他们需要访问集中管理的数据以便进行有效的电话推销。过去,这些数据是集中采集的,每天通过电子邮件提供给销售人员。然而,市场的压力和快速变化的销售情况迫使销售人员必须能访问需要的最新数据。不幸的是,IT经理们坚决拒绝允许远程用户访问数据库服务器,因为他们不希望通过公用的Internet发送用户名和口令。不过不要担心,我们将采取方法使大家都满意。
在标准的客户机/服务器程序中,你可能需要在应用程序启动时初始化数据库连接串,这意味着客户机必须能访问连接串信息,包括用户名和口令。但是,当不允许通过网络传送那些信息时,就需要在客户端和数据库之间不建立直接连接的情况下获取数据。
解决办法是在服务器上建立一个ASP页面,在示例代码中它的名字是getData.asp,它接受特定形式的POST数据,它等待一个 XML字符串,这个字符串里包含着建立ADO Command对象并运行存储过程或动态SQL语句所需的命令信息。如果信息充分,getData.asp执行存储过程并返回一个XML字符串,这个字符串里包含以XML格式表述的Recordset、一系列返回值或一个错误消息。对于返回数据的命令,客户端或者将返回的Recordset对象重新表述,或者使用XML文档对象模型(DOM)来查找返回值或错误消息。
ASP代码和所需接收的数据
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 _
("Could not load XML message." & _
"Description: " & _
xml.parseError.reason & _
"<br>Line: " & xml.parseError.Line)
End If
它查找元素commandtext的取值或元素returnsdata、returnsvalues的取值。因为查找这些元素取值的代码是相似的,因此这里只给出查找元素commandtext取值的代码:
Set N = xml.selectSingleNode _
("command/commandtext")
If N Is Nothing Then
Call responseError _
("Missing <sp_name> parameter.")
Else
sp_name = N.Text
End If
然后,页面创建一个Command对象,读取所有<param>元素,并为请求中存在的每一个元素创建一个参数,代码如下:
' create parameters, if any
set nodes = xml.selectNodes("command/param")
if nodes is nothing then
' no parameters
elseif nodes.length = 0 then
' no parameters
else
for each param in nodes
' Response.Write server.HTMLEncode(param.xml) & "<br>"
on error resume next
paramName = param.selectSingleNode("name").text
if err.number <> 0 then
call responseError("Parameter creation: " & _
"Unable to find the name " & "tag.")
end if
paramType = param.selectSingleNode("type").text
paramDirection = param.selectSingleNode("direction").text
paramSize = param.selectSingleNode("size").text
paramValue = param.selectSingleNode("value").text
if err.number <> 0 then
call responseError("The parameter named '" & _
paramName & "' was " & "missing one or more " & "required fields.")
end if
cm.Parameters.Append cm.CreateParameter(paramName,paramType, _
paramDirection,paramSize,paramValue)
if err.number <> 0 then
call responseError("Unable to create or " & "append the parameter " & _
"named '" & paramName & ".' " & err.description)
Response.end
end if
next
on error goto 0
end if
最后,页面打开一个链接并执行请求,对于存储过程,它使用不返回数据的adExecuteNoRecords选项。
set conn =
Server.CreateObject_
("ADODB.Connection")
conn.Mode=adModeReadWrite
conn.open _
Application("ConnectionString")
set cm.ActiveConnection=conn
' retrieve the data
if not returnsData then
cm.Execute
else
set R = server.CreateObject _
("ADODB.Recordset")
R.CursorLocation = adUseClient
R.Open cm, ,adOpenStatic, _
adLockReadOnly
end if
如果命令返回数据,变量returnsData的值将为True,页面以XML文档方式将recordset的结果返回给客户机:
' return the data, if required
if returnsData then
R.Save Response, adPersistXML
if err.number <> 0 then
call responseError _
("Recordset Save Error " & _
"on command '" & CommandText & _
"': " & Err.Description)
Response.end
end if
...
如果命令以输出参数返回值,页面将返回包含这些值的XML字符串。该文档的根是一个,每一个返回值对应一个子元素(参见本文可下载例程代码)。
如果产生错误,页面使用responseError子程序格式化并返回一个XML字符串。参数sDescription中包含了错误文本:
Sub responseError(sDescription)
Response.Write _
"<response><data>Error: " & sDescription & "</data></response>"
Response.end
End Sub
上面的图中显示了运行中的一个实例。应用程序在左边的<div>标记里显示一个客户名列表。每个客户旁边有两个链接:Purchase History和Recent Purchase。当用户点击其中之一时,客户端程序运行一个存储过程并在右边的<div>标记里显示结果。
为了显示这一方案的灵活性,虽然都使用到getData.asp,但三个返回数据的操作以互不相同的方式工作。对客户列表的查询涉及到动态SQL。对Purchase History的查询运行了一个称为CustOrderHist的存储过程,它附带在Northwind数据库中,并且返回一个recordset。对Recent Purchase的查询运行了一个称为RecentPurchaseByCustomerID的存储过程,它接受一个输入参数CustomerID并在一个输出参数ProductName中返回该客户最近购买的产品的名称。过程的定义是:
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、返回recordset的存储过程、还是在输出参数中返回值的存储过程,设置POST消息的过程差不多都完全相同。客户页面创建XML字符串<command>、创建XMLHTTPRequest对象,用Open方法将它设置为对getData.asp 页面中的URL使用POST方法、并制定对象以异步方式工作(Open方法中的False参数)。它使用Send方法发送字符串:
set xhttp = createObject("msxml2.XMLHTTP")
xhttp.open "POST", "http://localhost/myWeb/getData.asp", False
xhttp.send s
注意:在Open方法中使用的URL必须是一个完整的URL,而不是一个相对URL。换句话说,下面的代码不能工作,因为URL是不完整的:
xhttp.open "POST", "getData.asp", False
————转载自赛迪网
---- 北京网络工具(NetTool)斑竹
Oicq:8762220 Icq:89170510
E-Mail:[email protected]
|
|