明輝手游網中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

ASP實用大全-實戰(zhàn)ASP(7)

[摘要]在個人電腦上使用單機版應用軟件的時代很快就要過去了,現(xiàn)在大部分的應用程序都開發(fā)出網絡版或大都需要共享網絡上豐富的數(shù)據(jù)資源。我們雖然寫了很長時間基于客戶端/服務器的應用程序,但是這些程序大部分只是運行在小型的局域網內部。然而,有很多客觀的原因要求我們要修改這些程序以使它們能夠運行在一個企業(yè)的內部網甚...

     在個人電腦上使用單機版應用軟件的時代很快就要過去了,現(xiàn)在大部分的應用程序都開發(fā)出網絡版或大都需要共享網絡上豐富的數(shù)據(jù)資源。我們雖然寫了很長時間基于客戶端/服務器的應用程序,但是這些程序大部分只是運行在小型的局域網內部。然而,有很多客觀的原因要求我們要修改這些程序以使它們能夠運行在一個企業(yè)的內部網甚至是國際互聯(lián)網。

  是什么原因迫使我們做呢?首先,隨著一個企業(yè)的規(guī)模逐漸擴大,公司可能會跨地區(qū)甚至跨國經營,每個分公司的員工的數(shù)量也會逐年增多,這些在外地的員工肯定需要頻繁地訪問總公司的數(shù)據(jù)庫資源;其次,集中應用程序的數(shù)據(jù)資源,能夠使你更好的監(jiān)控數(shù)據(jù)庫的訪問和使用情況。第三,你可以通過從一個集中的位置獲取全局應用程序設置,從而維護和更新它們,最終達到緩減應用程序更新的目的。第四,盡量從Web服務器上訪問數(shù)據(jù)庫而不是從客戶端上訪問數(shù)據(jù)庫,這樣可以避免通過網絡傳送登錄信息和客戶密碼,從而避免安全隱患;而且,使用瀏覽器從后臺獲取數(shù)據(jù),這樣能夠避免刷新整個頁面。

  這就要求我們創(chuàng)建一個運行于互聯(lián)網上的應用程序,而假如想創(chuàng)建一個運行在HTTP協(xié)議上的VB程序,那么關鍵就是使用XML和XMLHTTPRequest對象。這個對象是Microsoft XML分析器(msxml.dll)的一部分,XMLHTTPRequest對象可以讓你通過HTTP向遠程服務器發(fā)送GET和POST請求,運行在遠程服務器上的程序接收這個請求,翻譯出它的內容,返回數(shù)據(jù)或者一個錯誤頁面到調用它的應用程序。對網絡編程有一些研究的朋友會看出我這個設想很象SOAP,但是在這里我不想使用SOAP,因為如果那樣的話會使程序變得很復雜。

  想要改變一個完全獨立的客戶端單機版程序是不太現(xiàn)實的,但即使如此,從一個集中的服務器上下載應用程序設置也比使用本地的INI文件或Windows注冊標有更大的獨立性和靈活性。舉例來說,假設你有一支手機銷售隊伍,他們需要訪問集中化的信息來更有效的銷售手機,每天,總公司集中收集數(shù)據(jù),然后用電子郵件的形式發(fā)送給銷售人員。然而,市場的壓力和迅速變化的銷售形式勢必使銷售人員要訪問最新的數(shù)據(jù)信息。但是,網絡管理員卻堅持拒絕讓在遠程客戶端的銷售人員訪問總公司數(shù)據(jù)庫服務器,因為他們不想通過公用的網絡發(fā)送用戶名和登錄密碼。因此勢必要使用一種新的技術代替基于客戶端/服務器的技術,不要著急,我想看完本文你就會解決這個問題的。

  讓我們先分析一下客戶端/服務器應用程序。在一個標準的客戶端/服務器應用程序中,在應用程序開始時,你能夠初始化數(shù)據(jù)庫連接字符串,這就意味著,客戶有使用數(shù)據(jù)庫連接字符串的權利,這包括用戶名和口令。但是客觀情況如果不允許你在網絡上發(fā)送這些信息的話,你就必需在不聯(lián)接數(shù)據(jù)庫的情況下直接從客戶端取得數(shù)據(jù)發(fā)送給客戶。那么解決方案之一就是在服務器上創(chuàng)建一個ASP頁(在本例中稱為getData.asp)接收特定格式的POST數(shù)據(jù),它要求一個包含XML字符串,用來創(chuàng)建ADO對象并運行存儲過程或動態(tài)SQL語句命令。如果信息有效的話,getData.asp執(zhí)行存儲過程,并返回一個XML格式的數(shù)據(jù)集、返回值列表或錯誤頁面信息的XML字符串。對于返回數(shù)據(jù)的命令,客戶端要么重新實例化要么返回值或使用XML DOM(Document Object Model文檔對象模型)格式的錯誤頁面。

  好,下面就讓我們來討論一下如何實現(xiàn)這個頁面吧!

  getData.asp頁面首先使用一個DOMDocument對象來保存客戶端發(fā)送的數(shù)據(jù):

  '創(chuàng)建DOMDocument對象
  Set xml = Server.CreateObject ("msxml2.DOMDocument")
  xml.async = False

  然后,它裝載POST數(shù)據(jù)

  '裝載POST數(shù)據(jù)
  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> 參數(shù)。")
  Else sp_name = N.Text
  End If

  接著,應該讓頁面創(chuàng)建一個Command對象,讀入所有<param>元素,并且為request中的每一個元素創(chuàng)建一個參數(shù)。最后,讓頁面打開一個連接使用存儲過程adExecuteNoRecords選項來執(zhí)行request。

  set conn = Server.CreateObject("ADODB.Connection")
  conn.Mode=adModeReadWrite
  conn.open Application("ConnectionString")
  set cm.ActiveConnection=conn
  ' 返回數(shù)據(jù)
  if not returnsData then
    cm.Execute
  else
   set R = server.CreateObject("ADODB.Recordset")
   R.CursorLocation = adUseClient
   R.Open cm, ,adOpenStatic, adLockReadOnly
  end if

  如果能夠返回數(shù)據(jù)的話,那么returnData變量就為真值,并且把結果數(shù)據(jù)集返回到客戶端,仍然是一個XML文檔。

  if returnsData then
   R.Save Response, adPersistXML
    if err.number <> 0 then
     call responseError ("數(shù)據(jù)集發(fā)生存儲錯誤" & "在命令'" & CommandText & "': " &    Err.Description)
     Response.end
    end if

  如果輸出參數(shù)返回值,那么這個頁面將返回一個包含這些值的XML字符串。文檔的根元素是一個<values>標記,每一個返回值都有其相應的子元素,如果發(fā)生任何錯誤,頁面都會格式化并返回一個包含錯誤信息的XML字符串:

  Sub responseError(sDescription)
   Response.Write "<response><data>錯誤: " & sDescription & "</data></response>"
   Response.end
  End Sub

  假設在我們前面所說的例子中,我們想在應用程序中顯示區(qū)域的左半邊顯示客戶的姓名列表,再在每個客戶姓名后面加上兩個鏈接:Purchase History和Recent Purchase。當用戶點擊其中的一個鏈接,客戶程序就會運行一個存儲過程并在右邊區(qū)域顯示出結果。 為了顯示這個想法的靈活性,我想讓用于返回數(shù)據(jù)的三個操作單元執(zhí)行不同的工作過程,它們都調用getData.asp。首先,通過調用CustOrderHist來運行一個存儲過程,返回客戶的Purchase History,它搜索Northwind數(shù)據(jù)庫(為了方便起見我使用MS SQL中自帶的數(shù)據(jù)庫)并返回一個數(shù)據(jù)集。用于返回Recent Purchase 的查詢語句運行一個叫RecentPurchaseByCustomerID的存儲過程,來接收輸入的CustomerID參數(shù)并通過ProductName參數(shù)返回最近顧客購買的商品名。定義其處理過程相應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

  不管你的查詢語句中含有動態(tài)SQL語句還是含有返回記錄集的存儲過程或是輸出一個返回值,其處理POST消息的方法是一樣的:

  set xhttp = createObject ("msxml2.XMLHTTP")
  xhttp.open "POST", "http://localhost/myWeb/ getData.asp", False
  xhttp.send s

  好了,現(xiàn)在讓我們看一看如何發(fā)送和接收數(shù)據(jù)

  客戶端的XML信息是由一個<command>元素和一些子元素組成:<commandtext>元素包含了存儲過程的名稱,<returnsdata>元素告訴服務器,客戶端是否要求接收返回數(shù)據(jù),<param>元素包含參數(shù)信息。如果不使用參數(shù)的話,那么最簡單的發(fā)送字符串查詢就象下面這樣:

  <command>
  。糲ommandtext>

  存儲過程或動態(tài)SQL語句

 。/commandtext>
   <returnsvalues>True</returnsvalues>
  </command>

  你可以為每一個參數(shù)使用一個<param>元素,來添加參數(shù)。每個<param>元素有五個子元素:name,type,direction,size和value。子元素的順序可以隨意調換,但是所有的元素都應當有不能缺少,我通常按照定義一個ADO對象的值的順序來定義它們。舉例來說,CustOrderHist存儲過程需要一個CustomID參數(shù),所以用來創(chuàng)建發(fā)送到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常量是不在客戶端定義的-這就是它們?yōu)槭裁词褂茫? %>標記圍起來的原因。服務器在發(fā)送響應之前使用正確的值取代它們。getData.asp頁有一個Response.ContentType,它的屬性為"text/xml",這樣,你就可以使用ResponseXML屬性來返回結果了。當請求返回紀錄,你就可以創(chuàng)建一個Recordset對象并且使用XMLHTTP來打開它:

  Dim R
   set R = createObject("ADODB.Recordset")
   R.open xhttp.responseXML

  當查詢語句返回數(shù)據(jù)時,通過設置XMLHTTPRequest對象的responseXML屬性來創(chuàng)建一個DOMDocument:

  Dim xml
   set xml = xhttp.responseXML

  輸出參數(shù)的XML字符串的每個返回值都包含一個元素,它們都是根元素<values>的直接子元素,例如:

  <?xml version=""1.0"" encoding=""gb2312""?>
 。紇alues>
 。紁aramname>value</paramname>
  。紁aramname>value</paramname>
 。/values>

  如果你的數(shù)據(jù)使用別的國家的文字,你可能需要把編碼屬性用相應的編碼替換,例如對于大部分歐洲語言,可以使用ISO-8859-1

  客戶端頁面使用返回的數(shù)據(jù)來格式化一個HTML字符串用于顯示,如:

  document.all("details").innerHTML = <一些格式化的HTML字符串>

  前面我們已經介紹了使用ASP和XML混合編程,那是因為ASP頁面能夠很容易讓我們看清應用程序正在做什么,但是你如果你不想使用ASP的話,你也可以使用任何你熟悉的技術去創(chuàng)建一個客戶端程序。下面,我提供了一段VB代碼,它的功能和ASP頁面一樣,也可以顯示相同的數(shù)據(jù),但是這個VB程序不會創(chuàng)建發(fā)送到服務器的XML字符串。它通過運行一個名叫Initialize的存儲過程,從服務器取回XML字符串,來查詢ClientCommands表的內容。

  ClientCommands表包括兩個域:command_name域和command_xml域?蛻舳顺绦蛐枰齻特定的command_name域:getCustomerList,CustOrderHist和RecentPurchaseByCustomerID。每一個命令的command_xml域包括程序發(fā)送到getData.asp頁面的XML字符串,這樣,就可以集中控制XML字符串了,就象存儲過程名字所表現(xiàn)的意思一樣,在發(fā)送XML字符串到getData.asp之前,客戶端程序使用XML DOM來設置存儲過程的參數(shù)值。我提供的代碼,包含了用于定義Initialize過程和用于創(chuàng)建ClientCommands表的SQL語句。

  我提供的例程中還說明了如何使用XHTTPRequest對象實現(xiàn)我在本文一開始時許下的承諾:任何遠程的機器上的應用程序都可以訪問getData.asp;當然,你也可以通過設置IIS和NTFS權限來限制訪問ASP頁面;你可以在服務器上而不是客戶機上存儲全局應用程序設置;你可以避免通過網絡發(fā)送數(shù)據(jù)庫用戶名和密碼所帶來的隱患性。還有,在IE中,應用程序可以只顯示需要的數(shù)據(jù)而不用刷新整個頁面。