用VB和XML建立集中式應用程序

發表于:2007-07-14來源:作者:點擊數: 標簽:
作者:青蘋果工作室編譯 很長一段時間以來,Visual Basic 程序員 都是在編寫客戶機/ 服務器 程序,這些程序工作在私有 網絡 上。但是這種在一臺PC機上安裝一個獨立應用程序的時代正在很快地結束,客戶機/服務器結構不再能滿足需要,現在的大多數商用程序需要
作者:青蘋果工作室編譯

  很長一段時間以來,Visual Basic程序員都是在編寫客戶機/服務器程序,這些程序工作在私有網絡上。但是這種在一臺PC機上安裝一個獨立應用程序的時代正在很快地結束,客戶機/服務器結構不再能滿足需要,現在的大多數商用程序需要共享數據。

為什么要建立基于Intr.net和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消息的過程差不多都完全相同??蛻繇撁鎰摻╔ML字符串<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

用VB和XML建立集中式應用程序(下)
(作者:青蘋果工作室編譯 2001年03月19日 15:41)

傳送消息及接收數據
  客戶端的XML消息由一個帶有幾個子元素的<command>元素構成:一個包含著存儲過程名稱的<commandtext>元素;一個<returnsdata> 元素,它告訴服務器客戶端是否期待返回數據;返回零個還是多個包含著參數信息的<param>元素。在這個最簡單的沒有參數的情況下,傳送的字符串查詢類似于:

  <command>

   <commandtext>

     StoredProc or Dynamic SQL

   </commandtext>

   <returnsvalues>True</returnsvalues>

  </command>

  要添加參數,就需要為每一個參數添加一個<param>元素。每個<param>元素有五個子元素:<name>、<type>、<direction>、<size>和<value>。五個子元素的先后次序無關緊要,但都是必須的。通常,依照定義ADO Parameter對象所需的次序定義它們。例如,存儲過程CustOrderHist需要一個CustomerID參數,所以創建傳送到 detData.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-Request對象的responseXML屬性來打開它:

  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=""ISO-8859-1""?>

  <values>

   <paramname>value</paramname>

   <paramname>value</paramname>

  </values>

  如果數據包含外語字符,就有可能需要修改encoding屬性。ISO-8859-1編碼能很好地支持大多數西歐語言。

  各種情況下,客戶端頁面都會使用返回值來格式化一個HTML字符串,這個字符串放在屏幕右側的div標記里的??蛻舳隧撁媸褂胐iv對象的innerHTML屬性來進行顯示:

  document.all("details").innerHTML =

   <some formatted html string>

為不同類型的客戶端服務
  ASP頁面讓我們方便地看到應用程序到底都做了些什么,但你能用相同的技術建立使用任何技術的客戶端程序??上螺d代碼中包含了像ASP頁面一樣地顯示數據的Visual Basic工程文件,但是VB工程并不創建它傳送到服務器上的XML字符串。相反,在啟動時它通過一個稱為Initialize的存儲過程從服務器查詢它們,這個過程簡單地從叫做ClientCommands的數據表里查找這些內容。

  數據表ClientCommands包含兩個字段:command_name和command_xml字段??蛻舳顺绦蚪邮杖齻€特定的command_name:getCustomerList、CustOrderHist和recentPurchaseByCustomerID。這些命令的command_xml字段包含了程序發送到getData.asp頁面的XML字符串,如此就集中地控制了XML字符串的格式以及存儲過程的“真實”名稱。在將XML字符串發送到getData.asp前,客戶端程序使用XML DOM來設置存儲過程的參數值??上螺d的代碼包含定義Initialize過程和創建并維護數據表ClientCommands的SQL代碼。

右圖顯示了 VB客戶端程序。圖中,示例程序的VB版本顯示了同基于瀏覽器版本大致相同的信息,只不過它使用的是bound data grid控件,而不是 HTML。
  示例應用程序演示了使用XHTTPRequest對象來兌現本文開頭中所做的許諾。應用程序工作在能訪問getData.asp的任何遠程計算機內。我們能通過IIS或NTSF權限設置限制對ASP頁面的訪問,并且能在服務器而不是客戶機上保存應用程序的全局設置。這樣就避免了通過網絡發送數據庫的用戶名和口令,而且在IE中應用程序按照需要顯示數據而不是刷新整個頁面。

結語
  在該應用的正式產品代碼中,我們能用很多種方法使應用程序更有效。比如,我們能從ASP頁面中去掉數據查詢代碼,并將其放到一個COM應用程序中;或者,我們還可以創建XSLT變換器以顯示返回的數據。最后,通過放棄動態頁面更新,并在服務器上進行XSLT變換,我們能夠擴大客戶端程序的應用范圍?,F在需要的做的,就是去嘗試。

原文轉自:http://www.anti-gravitydesign.com

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97