近幾年很流行 Ajax,而 Ajax 的本質就 是 XMLHttpRequest,是客戶端 XMLHttpRequest 對象的使用。相對于 Ajax,服務端 XMLHTTP 就是在服務端使 用 XMLHttpRequest 對象了。雖然說,在服務端使用異步請求是比較不方便的,但是做為可以服務端發送 HTTP 請求的組件,學習一下也是 沒有壞處的。[喝小酒的網摘]http://blog.const.net.cn/a/2589.htm
這里,我講的是在 ASP 環境中使用服務端 XMLHttpRequest,并以 JScript 做為演示代碼的語言,因此,你需要了解 ASP 以及 JScript。
服 務端 XMLHTTP,通常會用在獲取遠程主機的網頁或者其他內容,新聞聚合系統一般就是使用服務端 XMLHTTP 對象來獲取要聚合的 Feed 的 內容,然后使用 XMLDOM 對象來分析 Feed 的內容,取出新聞的標題、作者、內容等信息,再存在數據庫中,然后將若干個數據源的新聞一起顯示在 一起。抓蝦就是這樣一個新聞聚合器,但是它不是用 ASP 寫的就是了 :)
在 ASP 中,我們可以用以下代碼來創建一個 ServerXMLHTTP 對象,而這個對象,就是我們在服務端進行一切操作的基礎。
程序代碼:
// demo code from xujiwei
// @website: http://www.xujiwei.cn/
var xmlhttp = new ActiveXObject("MSXML2.ServerXMLHTTP.5.0");
首先來了解一下 ServerXMLHTTP 對象有哪些比較有用的方法:
1. abort 這個方法用于取消 XMLHTTP 的請求。如果 XMLHTTP 對象以異步方式發送請求,如果到達一定的時間請求仍然沒有返回,就可以使用這個方法來取消請求。
2. getAllResponseHeaders 這個方法的返回值是一個字符串,相當于 HTTP 請求的頭部去掉了請求方法、URI和協議版本信息。
3. getResponseHeader 這個方法用來獲取指定頭部信息,比較有用的就是可以用來獲取返回數據的 Content-Type、Referer 等。
4. open 使用指定的請求方法、URI和同步方式以及認證信息等初始化一個請求。
5. send 發 送 HTTP 請求,等待接收響應數據,注意,如果是以同步方式發送請求,send方法調用后不會立即返回,而是等到請求完成后才會返回,而以異步方法請 求時,則會立即返回。另外,send方法帶有一個可選參數body,表示要發送的數據,這在使用 POST 方法時比較有用。
6. setTimeout 設置 ServerXMLHTTP 對象的 4 個超時時間,分別是:域名解析、連接服務器、發送數據、接收響應。可以通過設置相應的超時時間來控制 ServerXMLHTTP 對象,以免 ServerXMLHTTP 不能及時返回而造成程序停止響應。
7. setRequestHeader 設 置請求的 Header,在客戶端 XMLHttpRequest中,通常用來設置請求的數據類型,或者標識請求的方法等等,例如 jquery 會增加 頭部標識 X-Request-With,表示請求是從 XMLHttpRequest 對象發出,以方便服務端做出相應的動作。
8. waitForResponse 在使用異步方式發送請求時,可以用這個方法來控制請求的進程。在服務端腳本中,不可以像客戶端那樣直接使用回調函數來控制異步請求,也沒有相應的函數來使用程序休眠一定的時間,因此,為了等待請求返回,我們可以使用這個方法來等待一定時間。
另外,還有其他一些方法,如 getOption、setOption、setProxy 等,這些方法用得比較少,因此這里不再介紹,需要了解的朋友可以查閱 MSDN。
接下來,再看看 ServerXMLHTTP 對象的屬性:
1. onreadystatechange XMLHTTP 對象狀態改變時的回調函數,這個屬性為異步操作奠定了一個基礎,可以讓程序在不用查詢 XMLHTTP 對象狀態的情況獲知 XMLHTTP 操作是否已經完成。
2. readyState XMLHTTP 對象狀態,有 5 個值,從 0 到 4,分別代表的意思是:
0 - 未初始化,剛使用 new ActiveXObject("MSXML.ServerXMLHTTP.5.0") 創建時對象所處的狀態
1 - 載入中,這個時候,已經調用了 open 方法,但是還沒有使用 send 方法發送數據
2 - 已經載入,已經調用了 send 方法發送數據,但是還沒有可用的響應流
3 - 正在交互,正在接收數據,這個時候可以使用 responseBody 和 responseText 屬性來獲取已經得到的部分數據了
4 - 完成請求,全部數據已經接收完成
通常情況下,我們只需要判斷一下狀態 4 即可,這個時候數據已經全部載入,使用 responseBody 或 responseText 屬性就能獲取需要的數據。
3. status HTTP 響應狀態碼,正常情況應該為 200,如果請求的資源不存在,就會返回 404,還有其他狀態碼如服務器錯誤 500 等。
4. statusText HTTP 響應狀態文本,用于描述響應狀態碼所代表的意思,諸如 200 OK 中的 OK,404 Not Found 中的 Not Found
5. responseBody 響應數據的字節數組,這在 VBScript 里是可以直接使用的,但是在 JScript 里就需要轉換過了。
6. responseText 以文本方式獲取響應數據
7. responseXML 將響應數據作為一個 XMLDOM 對象來返回,這在請求的數據是一個 XML 文檔時特別有用
8. responseStream 響應流對象,這個屬性不常用
在 ServerXMLHTTP中,異步請求不再是主要用途,往往是同步的請求用得更多,因為在服務端編程中,程序的執行是需要迅速結束并返回結果的,不像在 桌面程序中,有一個消息循環。這樣就導致了在服務端編程中,同步編程用得更多。當然,這并不是說異步請求沒有用處,在一定的情況下,異步請求會有很大的作 用。
1. 簡單的使用ServerXMLHTTP請求并顯示指定Url
首先來看一下很簡單的例子,使用ServerXMLHTTP請求Google的首頁并顯示出來:
程序代碼:
<%@LANGUAGE="JScript" CODEPAGE="65001"%>
<%
// code from xujiwei
// http://www.xujiwei.cn
var url = "http://www.google.cn";
var xmlhttp = new ActiveXObject("MSXML2.ServerXMLHTTP.5.0");
xmlhttp.open("GET", url, false);
xmlhttp.send("");
Response.BinaryWrite(xmlhttp.responseBody);
xmlhttp = null;
%>
在瀏覽器查看這個頁面,你就可以看到Google的首頁了:
圖片附件
但是,我們可以看到,這里的Logo圖片是沒有顯示的,因為這個logo在網頁源代碼里是以相對路徑的方式來指定的:
<img src=/intl/zh-CN/images/logo_cn.gif width=286 height=110 border=0 alt="Google" title="Google">
但是,我們的測試服務器里并沒有這個圖片文件,因此瀏覽器就會顯示此圖片的替代文字“Google”。
這 里我使用了xmlhttp的responseBody屬性,這是因為,在不知道所請求的網頁是使用什么編碼的情況下,可以讓瀏覽器來處理這個問題,而不用 在服務器處理編碼。如果要在服務器處理編碼,你必需知道你所請求的URL所返回的內容是使用什么編碼的,并且正確的將返回內容進行轉碼以使得客戶端瀏覽器 能正常的顯示。
例如,我們請求Baidu的首頁,就會因為編碼問題而導致頁面完全錯亂:
圖片附件
所以,使用responseText或者responseBody,完全取決于我們的需要,并不是一成不變的,或者,在某些時候,我們要使用的并不是這兩個中的一個,而是responseXML:)
2. 設置超時
在 使用ServerXMLHTTP發送同步請求時,整個ASP程序的執行是被阻塞了的,也就意味著在開始發送請求到請求完成響應這段時間里,我們是做不了任 何事情的。那么這里就有幾個問題,如果所請求的域名解析很慢怎么辦?如果程序運行的服務器與請求的服務器之間的網絡環境比較差導致連接很慢怎么辦?如果要 發送的數據量很大但是帶寬不夠怎么辦?同樣如果響應的數據量很大但是帶寬不夠怎么辦?
服務器所在環境及網絡條件我們是無法改善的,因為,面對這些問題,我們只能采取回避的策略,即如果碰到這些問題,我們就直接丟掉這個請求。這時,ServerXMLHTTP的超時機制就有很大的用處了。
在 前一篇中,我介紹了ServerXMLHTTP的常用方法,其中有一個setTimeouts方法,就是用來設置ServerXMLHTTP對象的四個超 時時間,分別是:域名解析超時時間(resolveTimeout)、連接超時時間(connectTimeout)、數據發送超時時間 (sendTimeout)、數據接收超時時間(receiveTimeout)。這四個超時時間所代表的意義可以從它們的字面來理解,它們分別對應了這 一節開頭所提出一的四個問題。
在不使用setTimeouts方法進行設置的情況下,域名解析超時時間(resolveTimeout)是 無限的,即不會在域名解析時產生超時,連接超時時間(connectTimeout)的默認值為60秒,數據發送超時時間(sendTimeout)的默 認值為30秒,數據接收超時時間(receiveTimeout)的默認值也是30秒。
通常情況下,我們不需要默認值中所指定的那么長的超時時間,因為碰到了最壞的情況下,在一個頁面顯示時,訪客將要面對2分鐘左右的無響應時間,這時訪客往往認為這個頁面是無效的并且會離開這個頁面。
所 以我們要做的就是給ServerXMLHTTP設置一個較短的超時時間,一般情況下,域名解析和連接遠程服務器都可以在2秒內完成,發送數據時間視數據量 而定,如果只是使用GET請求,這個數據量是很小的,也可以在2秒內完成,而響應,則可以稍微長一點,定在10秒左右,超過10秒時可以認為遠程服務器沒 有響應。
需要注意的是,setTimeouts方法所使用的參數單位是以毫秒為單位的,也就是說,如果 要指定2秒的超時時間,所用的參數為2000。另外,setTimeouts的參數順序也是固定的,按順序為:域名解析超時時間 (resolveTimeout)、連接超時時間(connectTimeout)、數據發送超時時間(sendTimeout)、數據接收超時時間 (receiveTimeout)。
那么,可以使用下面的代碼來完成超時設置:
程序代碼:
<%@LANGUAGE="JScript" CODEPAGE="65001"%>
<%
// code from xujiwei
// http://www.xujiwei.cn
var url = "http://www.google.com";
var xmlhttp = new ActiveXObject("MSXML2.ServerXMLHTTP.5.0");
// 設置超時時間,注意參數順序
xmlhttp.setTimeouts(2000, 2000, 2000, 10000);
xmlhttp.open("GET", url, false);
xmlhttp.send("");
Response.BinaryWrite(xmlhttp.responseBody);
xmlhttp = null;
%>
如 果在某個階段超時了,程序會拋出異常,在JScript里可以使用try...catch來捕獲,并根據ServerXMLHTTP對象的 readyState屬性來獲知是在哪個階段產生了超時異常。注意,同步請求時,超時異常會發生在調用send方法所在的行,例如上例中的 xmlhttp.send("")。
程序代碼:
<%@LANGUAGE="JScript" CODEPAGE="65001"%>
<%
// code from xujiwei
// http://www.xujiwei.cn
var url = "http://www.youtube.com/";
var xmlhttp = new ActiveXObject("MSXML2.ServerXMLHTTP.5.0");
// 設置超時時間,注意參數順序
xmlhttp.setTimeouts(2000, 2000, 2000, 10000);
xmlhttp.open("GET", url, false);
try {
xmlhttp.send("");
}
catch(e) {
Response.Write("發生異常:" + e.message + "<br/>");
// 判斷是否為超時錯誤
if(e.number == -2147012894) {
var step = "";
// 判斷超時錯誤發生所在的階段
switch(xmlhttp.readyState) {
case 1:
step = "解析域名或連接遠程服務器"
break;
case 2:
step = "發送請求";
break;
case 3:
step = "接收數據";
break;
default:
step = "未知階段";
}
Response.Write("在 " + step + " 時發生超時錯誤");
}
Response.End();
}
Response.BinaryWrite(xmlhttp.responseBody);
xmlhttp = null;
%>
3. 請求使用HTTP認證的頁面
呃,雖然說目前使用HTTP基本認證的已經少之又少,但是,總該知道ServerXMLHTTP有這么一個功能,可以直接實現HTTP基本認證。
在ServerXMLHTTP對象的open中,我們通常用到的只是它的前3個參數,即method、uri、async,但事實上,它還有另外兩個可選參數,即用于HTTP基本認證的username及password。
那么,如果某天,我們要使用ServerXMLHTTP訪問某個使用HTTP基本認證的網站,并且我們已經有了認證所需要的用戶名及密碼,那么可以使用以下密碼來訪問需要認證的內容:
程序代碼:
<%@LANGUAGE="JScript" CODEPAGE="65001"%>
<%
// code from xujiwei
// http://www.xujiwei.cn
// 訪問www.google.cn并不需要HTTP認證,這里只是作為一個演示
var url = "http://www.google.cn";
var xmlhttp = new ActiveXObject("MSXML2.ServerXMLHTTP.5.0");
// 用戶名和密碼分別為username和password
xmlhttp.open("GET", url, false, "username", "password");
xmlhttp.send("");
Response.BinaryWrite(xmlhttp.responseBody);
xmlhttp = null;
%>
4. 使用responseXML屬性
有時候,我們所需要的結果并不是文本的,而是一個XML文檔,譬如目前最常用的RSS。這個時候,responseXML屬性就是我們的不二選擇了。
使用responseXML屬性所得到的對象,就是一個DOMDocument對象,這個對象可以使用諸如selectNodes、selectSingleNode這樣的方法來操作XML文檔對象。
例如,我們可以利用ServerXMLHTTP抓取新浪新聞的RSS并顯示出來:
程序代碼:
<%@LANGUAGE="JScript" CODEPAGE="65001"%>
<%
// code from xujiwei
// http://www.xujiwei.cn
// 新浪新聞的RSS地址
var url = "http://rss.sina.com.cn/news/marquee/ddt.xml";
var xmlhttp = new ActiveXObject("MSXML2.ServerXMLHTTP.5.0");
xmlhttp.open("GET", url, false);
xmlhttp.send("");
var xml = xmlhttp.responseXML;
Response.Write("<h1>" + xml.selectSingleNode("/rss/channel/title").text + "</h1>");
var items = xml.selectNodes("/rss/channel/item");
for(var i = 0; i < items.length; i++) {
Response.Write("<h3>" + items[i].selectSingleNode("title").text + "</h3>");
Response.Write("<small>" + items[i].selectSingleNode("pubDate").text + "</small>");
Response.Write("<div>" + items[i].selectSingleNode("description").text + "</div><hr />");
}
items = null;
xmlhttp = null;
%>
這些如果弄明白了,寫一個RSS新聞聚合器就不是難事了。當然XMLDOM操作就不在本系列的范圍之類了。
服務端XMLHTTP(ServerXMLHTTP in ASP)進階應用-User Agent偽裝
這篇開始講講ServerXMLHTTP的進階應用。說是進階應用,但也就是講一些在基本應用里沒有講到的屬性或者方法之類:)
使用setRequestHeader偽裝User-Agent
User-Agent一般是服務端程序用來判斷客戶端瀏覽器、操作系統等信息的標志,它的說明可以參考Wiki,譬如在我的電腦 IE7 的UA就是:
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ;
SLCC1; .NET CLR 2.0.50727; .NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618)
可以看到,這個UA提供了不少信息,IE版本、Windows版本、.NET版本都有。再看看 Firefox 的:
Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3 (.NET CLR 3.5.30729)
UA的格式不是本文的重點,因此,如果需要了解UA的具體格式,可以去Google上找找,另外,在http://www.user-agents.org/這里可以找到目前大多數瀏覽器、搜索引擎Spider等的UA。
在我們使用 ServerXMLHTTP 發送請求時,它所用的 User-Agent 是以下內容:
Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)
但是,某些網站會限制這個UA的訪問,比如Google,如果我們使用以下代碼來請求Google的頁面,它使用的是默認UA“Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)”:
程序代碼:
<%@LANGUAGE="JScript" CODEPAGE="65001"%>
<%
// code from xujiwei
// http://www.xujiwei.cn
var url = "http://news.google.cn/?output=rss";
var xmlhttp = new ActiveXObject("MSXML2.ServerXMLHTTP.5.0");
xmlhttp.open("GET", url, false);
//xmlhttp.setRequestHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3 (.NET CLR 3.5.30729)");
xmlhttp.send("");
Response.BinaryWrite(xmlhttp.responseBody);
xmlhttp = null;
%>
你會發現,我們會收到一個 403 Forbidden 的錯誤:
圖片附件
為 了使得Google的RSS輸出程序能把我們的識別成正常的RSS閱讀或者一般瀏覽器,就需要在請求發出前設置 Request Header。要設 置 Request Header,只需要簡單的在調用 open 方法之后,send 方法之前使用 setRequestHeader 來設置就行 了,它的語法是 xmlhttp.setRequestHeader(key, value)。下面我們就讓Google的RSS輸出程序把我們的請求識 別為Firefox的請求:
程序代碼:
<%@LANGUAGE="JScript" CODEPAGE="65001"%>
<%
// code from xujiwei
// http://www.xujiwei.cn
var url = "http://news.google.cn/?output=rss";
var xmlhttp = new ActiveXObject("MSXML2.ServerXMLHTTP.5.0");
xmlhttp.open("GET", url, false);
// 設置 User Agent 為 Firefox 的UA
xmlhttp.setRequestHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3 (.NET CLR 3.5.30729)");
xmlhttp.send("");
Response.BinaryWrite(xmlhttp.responseBody);
xmlhttp = null;
%>
刷新瀏覽器,顯示出了Firefox的RSS預覽界面,獲取Google資訊RSS成功!
圖片附件
ok,我們能正確得到Google資訊的RSS了,再通過XMLDOM來操作返回的RSS文檔,就可以采集Google資訊那海量的新聞了。
[喝小酒的網摘]http://blog.const.net.cn/a/2589.htm
相關文章
發生msxml3.dll,錯誤800c0005錯誤的原因和解決方法Function getHTTPPage(url)
dim http
set http=Server.createobject("Microsoft.XMLHTTP")
Http.open "GET",url,false
Http.send()
if Http.readystate<
定時運行ASP文件在一定的時候,要定時的運行某個ASP文件去執行一個任務,如一個工廠在早上9點鐘要采集所有的電表的讀數,當然這要通過IN SQL連接到各個電表中,我們現在就是用一個ASP文件把IN SQL中表的讀數再集中到MS SQL中。
可能你看到的定時運行ASP文件的方法有多種,不過我現在要說的是一種簡單的方法,利用計劃任務就可簡單的實現。
首先,你要
安裝asppdf的幾種方式asppdf其實只有一個文件,也就是asppdf.dll,安裝的時候只需要復制到系統system32目錄下,然后regsvr32就可以了,當然你 也可以去官方網站下載asppdf.exe,安裝程序不會把asppdf.dll復制到system32,但他還是已經regsvr32了,并且有一些示 例代碼,也自動新建了一個虛擬目錄/asppdf。也就是說可以直接http://localhost/asppd
xmlhttp屬性和方法下面是WinHttpRequest component,不過我想也應該適用于xmlhttp,
前文有講到xmlhttp的屬性與方法,但覺得不是很完整,所以就有了下面的補充,下面的這些數據是來自于Microsoft,估計這個是比較全的,但里面提到的一些方法,例如屬性(property-get)Option,在xmlhttp中平時都沒有用過,又比如function SetTimeouts
asp中使用xmlhttp下載圖片代碼asp中使用xmlhttp下載圖片代碼:
<%
function getHTTPimg(url)
dim http
set http=server.createobject("MSXML2.XMLHTTP")
Http.open "GET",url,false
Http