AJAX(XMLHttpRequest)進(jìn)行跨域請求方法詳解
發(fā)表于:2010-1-11 瀏覽:965 作者:轉(zhuǎn)載遺失 來源:網(wǎng)絡(luò)轉(zhuǎn)載關(guān)鍵字:AJAX,詳解, 請求描述:注意:以下代碼請在Firefox3.5、Chrom e3.0、Safari4之后的版本中進(jìn)行
發(fā)表于:2010-1-11 瀏覽:965 作者:轉(zhuǎn)載遺失 來源:網(wǎng)絡(luò)轉(zhuǎn)載
關(guān)鍵字:AJAX,詳解, 請求
描述:注意:以下代碼請在Firefox3.5、Chrom e3.0、Safari4之后的版本中進(jìn)行測試。IE8的實(shí)現(xiàn)方法與其他瀏覽不同??缬蛘埱螅櫭剂x,就是一個站點(diǎn)中的資源去訪問另外一個不同域名站點(diǎn)上的資源。這種情況很常見
注意:以下代碼請在Firefox 3.5、Chrome 3.0、Safari 4之后的版本中進(jìn)行測試。IE8的實(shí)現(xiàn)方法與其他瀏覽不同。
跨域請求,顧名思義,就是一個站點(diǎn)中的資源去訪問另外一個不同域名站點(diǎn)上的資源。這種情況很常見,比如說通過 style 標(biāo)簽加載外部樣式表文件、通過 img 標(biāo)簽加載外部圖片、通過 script 標(biāo)簽加載外部腳本文件、通過 Webfont 加載字體文件等等。默認(rèn)情況下,腳本訪問文檔屬性等數(shù)據(jù)采用的是同源策略(Same origin policy)。
那么,什么是同源策略呢?如果兩個頁面的協(xié)議、域名和端口是完全相同的,那么它們就是同源的。同源策略是為了防止從一個地址加載的文檔或腳本訪問或者設(shè)置從另外一個地址加載的文檔的屬性。如果兩個頁面的主域名相同,則還可以通過設(shè)置 document.domain 屬性將它們認(rèn)為是同源的。
隨著 Web2.0 和 SNS 的興起,Web 應(yīng)用對跨域訪問的需求也越來越多,但是,在腳本中進(jìn)行跨域請求是受安全性限制的,Web 開發(fā)人員迫切需要提供一種更安全、方便的跨域請求方式來融合(Mashup )自己的 Web 應(yīng)用。這樣做的一個好處就是可以將請求分?jǐn)偟讲煌姆?wù)器,減輕單個服務(wù)器壓力以提高響應(yīng)速度;另外一個好處是可以將不同的業(yè)務(wù)邏輯分布到不同的服務(wù)器上以降低負(fù)載。
值得慶幸的是,跨域請求的標(biāo)準(zhǔn)已經(jīng)出臺,主流瀏覽器也已經(jīng)實(shí)現(xiàn)了這一標(biāo)準(zhǔn)。W3C 工作組中的 Web Applications Working Group(Web 應(yīng)用工作組)發(fā)布了一個 Cross-Origin Resource Sharing(跨域資源共享,該規(guī)范地址:http://www.w3.org/TR/access-control/和
下面我們就采用實(shí)際的例子說明 Cross-Origin Resource Sharing 是如何工作的。
1,簡單請求
,什么樣的請求算是簡單請求呢?簡單請求必須滿足下面2點(diǎn):
a ,只使用 GET 、POST 進(jìn)行的請求,這里的POST 只包括發(fā)送給服務(wù)器的數(shù)據(jù)類型(Content-Type )必須是 application/x-www-form-urlencoded、multipart/form-data 或者 text/plain中一個。 b ,HTTP 請求沒有設(shè)置自定義的請求頭,如我們常用的 X-JSON 。
先使用下面的代碼進(jìn)行測試:
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
然后,在服務(wù)器創(chuàng)建 CrossDomainRequest.aspx 的內(nèi)容如下:
<@ Page Language="C#" >
點(diǎn)擊 “開始測試” 按鈕,發(fā)送的請求和返回的響應(yīng)信息如下:
GET /SimpleCrossSiteRequests.aspx HTTP/1.1
Host: dotnet.aspx.cc
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; zh-CN; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer:
HTTP/1.x 200 OK
Date: Sun, 10 Jan 2010 13:52:00 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Access-Control-Allow-Origin: http://www.meng_xian_hui.com:801
Set-Cookie: ASP.NET_SessionId=wk5v5nrs5wbfi4rmpjy2jujb; path=/; HttpOnly Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 84
需要特別注意的是:在請求信息中,瀏覽器使用 Origin 這個 HTTP 頭來標(biāo)識該請求來自于
,頭來控制哪些域名的腳本可以訪問該資源。如果設(shè)置 Access-Control-Allow-Origin:*,則允許所有域名的腳本訪問該資源。如果有多個,則只需要使用逗號分隔開即可。
注意:在服務(wù)器端,Access-Control-Allow-Origin 響應(yīng)頭 http://www.meng_xian_hui.com:801 中的端口信息不能省略。
有人可能會想:自己發(fā)送請求頭會如何呢?比如
xhr.setRequestHeader("Origin","http://www.meng_xian_hui.com:801"); 實(shí)踐證明,自己設(shè)置 Origin 頭是不行的。
是不是現(xiàn)在就可以采用 XMLHttpRequest 來請求任意一個網(wǎng)站的數(shù)據(jù)呢?還是不行的。允許哪些域名可以訪問,還需要服務(wù)器來設(shè)置 Access-Control-Allow-Origin 頭來進(jìn)行授權(quán),具體的代碼是: Response.AddHeader("Access-Control-Allow-Origin",
"http://www.meng_xian_hui.com:801");
這行代碼就告訴瀏覽器,只有來自 http://www.meng_xian_hui.com:801 源下的腳本才可以進(jìn)行訪問。
好了,上面我們就完成了一個簡單的跨域請求,怎么樣?感覺還是不錯的吧。下面我們進(jìn)行一個“預(yù)檢”請求。
2,預(yù)檢請求
預(yù)檢請求首先需要向另外一個域名的資源發(fā)送一個 HTTP OPTIONS 請求頭,其目的就是為了判斷實(shí)際發(fā)送的請求是否是安全的。下面的2種情況需要進(jìn)行預(yù)檢:
a ,不是上面的簡單請求,比如使用Content-Type 為 application/xml 或 text/xml 的 POST 請求 b ,在請求中設(shè)置自定義頭,比如 X-JSON 、X-MENGXIANHUI 等
注意:在 iis 里進(jìn)行測試,必須在“應(yīng)用程序擴(kuò)展”里面配置 .aspx 擴(kuò)展的動作允許 OPTIONS 。 下面我們舉一個預(yù)檢的請求:
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
上面的例子我們發(fā)送 xml 格式的數(shù)據(jù),并且,發(fā)送一個非標(biāo)準(zhǔn)的HTTP 頭
POWERED-BY-MENGXIANHUI 來說明服務(wù)器端該如何設(shè)置響應(yīng)頭的。
在服務(wù)器端,PreflightedRequests.aspx 的內(nèi)容如下:
,<@ Page Language="C#" >
點(diǎn)擊“開始測試”按鈕,將會執(zhí)行下面的一系列請求。
,OPTIONS /PreflightedRequests.aspx HTTP/1.1
Host: dotnet.aspx.cc
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; zh-CN; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Origin: http://www.meng_xian_hui.com:801
Access-Control-Request-Method: POST
Access-Control-Request-Headers: powered-by-mengxianhui
HTTP/1.x 200 OK
Date: Sun, 10 Jan 2010 14:00:34 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Access-Control-Allow-Origin: http://www.meng_xian_hui.com:801
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: POWERED-BY-MENGXIANHUI
Access-Control-Max-Age: 30
Set-Cookie: ASP.NET_SessionId=5npqri55dl1k1zvij1tlw3re; path=/; HttpOnly
Cache-Control: private
Content-Length: 0
POST /PreflightedRequests.aspx HTTP/1.1
Host: dotnet.aspx.cc
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; zh-CN; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
POWERED-BY-MENGXIANHUI: Approve
Content-Type: application/xml; charset=UTF-8
Referer:
Origin: http://www.meng_xian_hui.com:801
Pragma: no-cache
Cache-Control: no-cache
HTTP/1.x 200 OK
Date: Sun, 10 Jan 2010 14:00:34 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Access-Control-Allow-Origin: http://www.meng_xian_hui.com:801
Set-Cookie: ASP.NET_SessionId=byvose45zmtbqy45d2a1jf2i; path=/; HttpOnly Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 65
以上的代碼反映了預(yù)檢請求的執(zhí)行過程:首先發(fā)送 OPTIONS 請求頭,用來向服務(wù)器咨詢服務(wù)器的更多信息,以便為后續(xù)的真實(shí)請求做準(zhǔn)備。比如是否支持 POST 方法等。值得注意的是:
瀏覽器還發(fā)送 Access-Control-Request-Method: POST 和 Access-Control-Request-Headers: powered-by-mengxianhui 請求頭。
注意:以上過程是第一次請求的時候的過程,如果在 30 秒內(nèi)重復(fù)點(diǎn)擊按鈕,你可以看不到 OPTIONS 這一過程。則執(zhí)行過程是這樣的:
POST /PreflightedRequests.aspx HTTP/1.1
Host: dotnet.aspx.cc
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; zh-CN; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
POWERED-BY-MENGXIANHUI: Approve
Content-Type: application/xml; charset=UTF-8
Referer:
Origin: http://www.meng_xian_hui.com:801
Pragma: no-cache
Cache-Control: no-cache
HTTP/1.x 200 OK
Date: Sun, 10 Jan 2010 14:06:32 GMT
Server: Microsoft-IIS/6.0
,X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Access-Control-Allow-Origin: http://www.meng_xian_hui.com:801
Set-Cookie: ASP.NET_SessionId=qs1c4urxywdbdx55u04pvual; path=/; HttpOnly Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 65
為什么會這樣?細(xì)心的童鞋可能注意到了,在服務(wù)器端有一行代碼
Response.AddHeader("Access-Control-Max-Age", "30"); 它是用來設(shè)置預(yù)檢的有效時間的,單位是秒。這一點(diǎn)要特別注意。
3,帶驗證信息的請求
身份驗證是Web 開發(fā)中經(jīng)常遇到的問題,在跨域請求中,默認(rèn)情況下是不發(fā)送驗證信息的。要想發(fā)送驗證信息,需要進(jìn)行withCredentials 屬性,下面就是一個簡單請求的例子:
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
點(diǎn)擊“開始測試”,我們可以檢測到下面的請求執(zhí)行過程:
GET /RequestsWithCredentials.aspx HTTP/1.1
Host: dotnet.aspx.cc
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; zh-CN; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer:
HTTP/1.x 200 OK
Date: Sun, 10 Jan 2010 14:12:26 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Access-Control-Allow-Origin: http://www.meng_xian_hui.com:801
Access-Control-Allow-Credentials: true
Set-Cookie: ASP.NET_SessionId=fn2zf0zq1cuwgf45fm5fw145; path=/; HttpOnly Set-Cookie: visit=1; expires=Sun, 10-Jan-2010 14:12:56 GMT; path=/