4.3.3 執(zhí)行其他的網(wǎng)頁(yè) asp 3.0和IIS 5.0的新特性之一就是引入了可編程的服務(wù)器端重定向(server-side redirection)的概念。這意味著,可以把一個(gè)網(wǎng)頁(yè)的控制和執(zhí)行轉(zhuǎn)到另外一個(gè)網(wǎng)頁(yè),而不需要在客戶端使用Response.Rdedirect方法。 1. 客戶端重定向帶來的問題 ASP編程人員通常使用Response.Redirect語(yǔ)句把一個(gè)頁(yè)面載入到當(dāng)前正在執(zhí)行的網(wǎng)頁(yè)。然而,許多人沒有意識(shí)到這條語(yǔ)句不會(huì)自動(dòng)地使服務(wù)器立即裝入和執(zhí)行新的網(wǎng)頁(yè)。其真正做的是把一個(gè)HTTP重定向報(bào)頭(redirection header)增加到由Web服務(wù)器發(fā)送給客戶的輸出流中。這個(gè)報(bào)頭如下: HTTP/1.1 302 Object Moved Location newpage.asp 在這個(gè)報(bào)頭中的標(biāo)準(zhǔn)HTTP狀態(tài)信息“302 Object Moved”,告知瀏覽器所要求的資源已經(jīng)發(fā)生移動(dòng)。Location報(bào)頭提供相應(yīng)的網(wǎng)頁(yè)地址。當(dāng)然這個(gè)地址不一定是真實(shí)的,現(xiàn)在正在做的事情就是“欺騙”瀏覽器,使瀏覽器認(rèn)為可在另一個(gè)位置上找到所需要的網(wǎng)頁(yè)。實(shí)際發(fā)生的是,服務(wù)器將執(zhí)行所請(qǐng)求的網(wǎng)頁(yè),但是通知瀏覽器需要的網(wǎng)頁(yè)已經(jīng)發(fā)生移動(dòng)。這就是在發(fā)送任何頁(yè)面的內(nèi)容到瀏覽器之前必須執(zhí)行Redirect方法的原因。 當(dāng)一個(gè)瀏覽器接受到“302 Object Moved”信息時(shí),中斷當(dāng)前的請(qǐng)求并為L(zhǎng)ocation值中指定的網(wǎng)頁(yè)發(fā)送一個(gè)新的請(qǐng)求。這與在網(wǎng)頁(yè)的<HEAD>段使用一個(gè)META HTTP-EQUIV標(biāo)記時(shí)的工作方式相同,前面給出的HTTP報(bào)頭還可寫為: <META HTTP-EQUIV=”REFRESH” CONTENT=”0;URL=newpage.asp”> 因此重定向?qū)嶋H上發(fā)生在客戶機(jī)端,而不是在服務(wù)器上。如果在這個(gè)連接的客戶端有一個(gè)代理服務(wù)器在使用的話,可能會(huì)引起顯示虛假消息。這就是在使用Response.Redirect時(shí),“The object you requested has been moved and can be found here”消息經(jīng)常在客戶機(jī)上顯示的原因,正確地使用緩沖通常可以防止這個(gè)問題。 在IIS 4.0或更早的版本中使用Response.Redirect時(shí),應(yīng)該在ASP網(wǎng)頁(yè)的開頭打開緩沖,然后在執(zhí)行Response.Redirect方法之前調(diào)用Response.Clear。當(dāng)然,在ASP 3.0中網(wǎng)頁(yè)緩沖的缺省狀態(tài)為打開,因此這不成問題。只要在執(zhí)行該語(yǔ)句之前使用Response.Clear,以前產(chǎn)生的輸出將不會(huì)發(fā)送給客戶。 2. 在ASP 3.0中服務(wù)器端的重定向 在ASP 3.0和IIS 5.0中,在幾乎所有情況下,通過使用兩個(gè)新的Server對(duì)象方法Execute和Transfer,可以避免使用客戶端重定向。這兩個(gè)方法使控制立即轉(zhuǎn)到另一個(gè)網(wǎng)頁(yè),該網(wǎng)頁(yè)可以是一個(gè)ASP網(wǎng)頁(yè)或者是任何其他的資源,例如一個(gè)HTTP網(wǎng)頁(yè)、壓縮文件或其他類型的文件。 它們之間的不同之處是:Execute方法“調(diào)用”另一個(gè)的網(wǎng)頁(yè),與在腳本代碼中調(diào)用一個(gè)子程序或函數(shù)非常相似。當(dāng)另一個(gè)網(wǎng)頁(yè)或資源已經(jīng)執(zhí)行完畢或傳送到客戶端時(shí),控制返回到原網(wǎng)頁(yè)中調(diào)用Execute方法的語(yǔ)句的下一條語(yǔ)句,并繼續(xù)執(zhí)行。而使用Transfer方法時(shí),控制不再返回到原頁(yè)面中,在控制傳送到的網(wǎng)頁(yè)或資源的末尾處,執(zhí)行過程停止。 當(dāng)前網(wǎng)頁(yè)的環(huán)境也傳送給了目標(biāo)網(wǎng)頁(yè)或資源,因此這兩個(gè)方法更有用。網(wǎng)頁(yè)環(huán)境包含了原有的ASP對(duì)象中的所有變量的值,例如Request、Response和session對(duì)象的集合以及它們的所有屬性。即使該網(wǎng)頁(yè)不在同一個(gè)虛擬應(yīng)用程序中,也將傳送application對(duì)象的環(huán)境。 結(jié)果是瀏覽器認(rèn)為它仍在接收原先的頁(yè)面,它并不了解服務(wù)器所做的事情。瀏覽器的地址欄一直顯示相同的URL,并且Back、Forward和Refresh按鈕正常地工作。在使用客戶端重定向時(shí),尤其是使用HTML META元素時(shí),情況通常不是這樣的。 傳送到新的頁(yè)面或資源的環(huán)境包括所有現(xiàn)存的事務(wù)狀態(tài)(transaction state)。當(dāng)前網(wǎng)頁(yè)的環(huán)境用ASP的ObjectContext對(duì)象(在第1章中已經(jīng)討論過)進(jìn)行封裝。如果需要將這個(gè)對(duì)象作為一個(gè)正在進(jìn)行的事務(wù)的一部分,可以在傳送控制的目的頁(yè)面中使用這個(gè)對(duì)象。 (1) Server對(duì)象的Execute和Transfer方法的使用 在前面的示例頁(yè)面中,可以試驗(yàn)使用Excute和Transfer方法。該頁(yè)面包含了在示例中已經(jīng)提供的另一個(gè)文件名字another_page.asp,它作為這兩個(gè)方法的缺省參數(shù)值,如圖4-13所示:
圖4-13 使用Execute和Transfer方法的屏幕 單擊Server.Execute和Server.Transfer方法的按鈕,提交到此窗體并重新裝載該窗體。在這個(gè)頁(yè)面頂部的腳本代碼查看是哪個(gè)按扭被單擊。如果是cmdExecute或cmdTransfer按鈕,則把當(dāng)前網(wǎng)頁(yè)的路徑寫入到輸出流中,然后調(diào)用相應(yīng)的方法,并傳送與該按鈕相聯(lián)系的文本框中的值,然后再把當(dāng)前頁(yè)面的路徑寫到輸出流中。 … If Len(Request.Form("cmdExecute")) Then strPath = Request.Form("txtExecPath") Response.Write "Currently executing the page: <B>" _ & Request.ServerVariables("SCRipT_NAME") & "</B><BR>" Server.Execute (strPath) Response.Write "Currently executing the page: <B>" _ & Request.ServerVariables("SCRIPT_NAME") & "</B><BR>" End If
If Len(Request.Form("cmdTransfer")) Then strPath = Request.Form("txtTransferPath") Response.Write "Currently executing the page: <B>" _ & Request.ServerVariables("SCRIPT_NAME") & "</B><BR>" Server.Transfer (strPath) End If … 當(dāng)單擊Server.Excute方法的按鈕時(shí),會(huì)看到當(dāng)前頁(yè)面的路徑,這是由上面代碼中的第一條Response.Write語(yǔ)句創(chuàng)建并顯示的。后面接著的內(nèi)容是來自被執(zhí)行的網(wǎng)頁(yè)(another_page.asp)的一些輸出內(nèi)容。在這之后是第二個(gè)Response.Write語(yǔ)句的輸出內(nèi)容,這表明控制又回到了原先的網(wǎng)頁(yè),屏幕如圖4-14所示:
圖4-14 Server.Excute方法的演示 頁(yè)面的兩條水平線之間的段落(顯示當(dāng)前執(zhí)行的網(wǎng)頁(yè)為show_server.asp)來自原先的網(wǎng)頁(yè)。在接下來的段落來自被執(zhí)行的網(wǎng)頁(yè)another_page.asp。下面是該頁(yè)面的完整代碼: <%@ LANGUAGE=VBSCRIPT %> <HR> Currently executing the page: <B>another_page.asp</B><BR> However the value of <B>Request.ServerVariables("SCRIPT_NAME")</B> is still <BR> <B><% = Request.ServerVariables("SCRIPT_NAME") %></B> because the <B>Request</B> collections hold<BR> the same values as they had in the page that executed this one.<BR>
<FORM ACTION="<% = Request.ServerVariables("HTTP_REFERER") %>" METHOD="POST"> <INPUT TYPE="SUBMIT" NAME="cmdOK" VALUE=" "> Return to the PRevious page<P> </FORM> <HR> 注意,該頁(yè)面執(zhí)行時(shí),不能使用Request.ServerVariables(“SCRIPT_NAME”)獲取它的路徑,因?yàn)榄h(huán)境仍然是原網(wǎng)頁(yè)的。我們不得不把頁(yè)面名作為文本寫入,因?yàn)閷?shí)在沒有辦法可以從ASP環(huán)境中直接獲取。 這里包括了一個(gè)返回前一個(gè)網(wǎng)頁(yè)的按鈕的原因是,通過在主網(wǎng)頁(yè)中單擊相對(duì)應(yīng)的按鈕,可以使用Server.Transfer方法調(diào)用這個(gè)頁(yè)面。這次看到了完全相同的輸出,只是沒有第二次路徑輸出,因?yàn)槭恰皞魉汀边@個(gè)頁(yè)面而不是“執(zhí)行”該頁(yè)面,所以控制不會(huì)回傳給原先的網(wǎng)頁(yè),如圖4-15所示:
圖4-16 執(zhí)行Server.Excute方法后的屏幕 3. SSI #exec指令的不足 遺憾的是Execute和Transfer方法一般不能與SSI的#exec指令一起工作,因?yàn)榘@個(gè)指令的.stm網(wǎng)頁(yè)會(huì)在調(diào)用它的ASP網(wǎng)頁(yè)的環(huán)境中運(yùn)行。在大多數(shù)情況下,它需要運(yùn)行于直接引用該網(wǎng)頁(yè)的一個(gè)獨(dú)立的環(huán)境中。 存在這樣的限制真是遺憾,如果沒有這種限制,我們通過Server.Execute執(zhí)行的網(wǎng)頁(yè)可以“不可見地”包含來自于ASP網(wǎng)頁(yè)的#exec指令。對(duì)前面的通過net stop和net start命令停止和啟動(dòng)Indexing Service的示例來說,它可能是一種理想的解決方案。 但是,我們必須求助于老的和已經(jīng)驗(yàn)證的方法。當(dāng)用戶單擊一個(gè)按鈕時(shí),簡(jiǎn)單地使用Response.Redirect方法來打開相關(guān)的網(wǎng)頁(yè): <% ‘Look for a command sent from the FORM section buttons If Len(Request.Form(“cmdStop”)) Then Response.Redirect(“exec/stop_cisvc.stm”) End If
If Len(Request.Form(“cmdStart”)) Then Response.Redirect(“exec/start_cisvc.stm”) End If %> 可以試著把使用#exec指令的一個(gè)SSI網(wǎng)頁(yè)的虛擬路徑輸入到示例頁(yè)面的Server.Execute和Server.Transfer方法的文本框中。前面使用過的#exec示例的虛擬路徑是“../ssi_cgi/exe/start_cisvc.stm”和“../ssi_cgi/exec/stop_cisvc.stm”。