天天看點

AJAX跨域資料獲得的研究

跨域需求其實由來已久,之前是公司的網站需要同不同域名下的同為公司網站的資料互動,但始終沒有做出來,當時時間也無法都花在這個上,是以就不了了之了。至今有技術界的朋友問起相關問題,由考慮後期伺服器架構優化網站效率優化與跨域有着必然的聯系,于是又撿起這個話題重新研究。

同源政策

說跨域則不得不說同源政策。所謂同源政策是W3C網絡标準制定的一個安全方面的限制,就比如說,我們可能之前在哪裡看到一個比較漂亮的背景圖檔,找到它的URL後就可以直接用在自己的網站裡,并不一定要下載下傳到自己的網站目錄中,例如CSS的代碼可以如下書寫

background-image:url(http://www.xxx.com/images/123.jpg);

即使此時自己的網站域名同這個圖檔的加載域名不同,也可以加載這個圖檔。

但同源政策并不是說這個,而相反是一種限制,尤其是在AJAX技術盛行的WEB2.0的今天,同源政策起到了它應有的保護作用,否則網絡安全将會大打折扣。

對于AJAX技術熟悉的朋友,都會知道通過JS前端的事件觸發(當然并不一定需要互動觸發才能實作AJAX互動)來同服務端進行AJAX的資料互動,從服務端傳回的資料可以是普通的HTML,XML也可以是JSON,但一般的互動都是同自己域名下的服務端檔案,用相對路徑引用居多,如果自己公司有多個域名,網絡布局各域名針對的業務功能都不同,但有域名A需要調用域名B下的資料,如果使用者通過普通AJAX調用 例如www.test1.com/index.html頁面調用 www.test2.com/getData.php下所得到的資料,但實際是無法獲得到的,AJAX的傳回會提示調用失敗的狀态。這個即是同源政策在其作用。

同源政策是指,網絡請求如何在同一域名下,同一通路端口下,同一請求方案下,才算做是同源,比如

http://www.my.com 和 http://www.my.cn 不是同源,因為域名不同

http://www.my.com 和 http://list.my.com 也看作不是同源,因為一個是頂級域名,一個是二級域名

http://www.my.com:80 和 http://www.my.com:8080 也不是同源,因為所通路的端口不同

http://www.my.com 和 https//www.my.com 也非同源 因為通路的請求方案(協定)不同,一個是http請求,一個是https的請求 

以上都是同源政策限制的通路方式,是以一般情況下,安全政策是禁止你通路非同源下的資源檔案的。

明天繼續

以上是同源政策的解釋,但注意,并非所有網際網路資源都受同源政策影響,最簡單就是我一開始說的那個圖檔的例子。 實際還有一些HTML資源也并不受同源政策影響,分别羅列在下面,當然不一定全面。

1 樣式表檔案

樣式表檔案作為網站的樣式提供者,書寫方式可以單獨為檔案,比如style.css,或者嵌入在html檔案中。當然後者不幹同源政策什麼事,主要說的是前者,單獨的style.css樣式表檔案,實際不受同源政策影響。具體解釋為:即使在我自己的網站中使用了新浪網上的樣式表檔案,樣式表依然可以很好的工作,隻要我的HTML的結構中有樣式表中的定義,自然會作用到頁面上而無需擔心沒有權限通路。

例如 <LINK rel="stylesheet" type="text/css" href="http://news.sina.com.cn/css/87/20110909/218/vweibo.css"> 雖然加載的是外部樣式表,但依然可以正常作用。

2 js用戶端腳本檔案

可能對js特性不太了解的技術人員并不知道可以引用網絡資源的js檔案,例如

<script language="javascript" src="http://code.jquery.com/jquery-latest.js"></script>

該段引用表示在頁面中引用最新版的jQuery架構檔案。因不受同源政策影響,故此js檔案可以正常加載,浏覽器也不會報錯。

值得注意的是,加載相關外部js檔案後,目前頁面的操作權就對這個js開放了,它通過一些dom更改語句可以任意修改你的頁面。是以一般在不了解外部js的功能情況下,不建議随意加載外部js。

當然HTML語言中有個比較特殊的東西,就是iframe,我們稱為架構的東西,這個可以加載任意的外部頁面,是以也不受限制,嚴格來講實際不是一馬事。當然别以為iframe可以加載網絡任意檔案你就可以為所欲為,它的限制是,你沒有辦法對iframe的内容進行任何操作,那怕就是簡單的獲得他的innerHTML也是不可以的。

以上為同源政策說明和實際的一些規避元素,了解這些基本知識之後,我們就可以去研究如何讓ajax跨域加載資料了。

一 使用新版本浏覽器的Access-Control-Allow-Origin屬性

該屬性一般不會用到,做跨域的時候我才接觸到。 在做的時候也從網上找了它的相關資料,但百度下并不多,不記得哪個網站上說的,該屬性是因為W3C考慮到同源政策規則限制了對于某些确實需要跨域的需求所做的協調。先不管其是否正确,先看看這個屬性具體作用。

該屬性表示目前頁面可允許的外來請求域,例如 php下的head設定代碼

<?php

header("Access-Control-Allow-Origin:http://www.test1.com");

?>

設定完之後,該頁面就表示可以接受從域www.test1.com發過來的請求,然後該頁面就會準備資料然後傳回。

需要注意的有幾點

1 該屬性的書寫一般用于被調用頁面,是以如果你把該屬性寫在發起調用的頁面,例如www.test1.com/index.php 則無法正常響應,因為被請求頁面沒有設定。是以更詳細來說,如果你希望你自己的網站能夠調用比如sina網的某些新聞資訊,通過ajax調用,就必須得在sina的相關頁面上去設定這個參數,且加上自己的域名,當然一般不會有sina的頁面修改權限,故這個是行不通的,除非用它公開的調用接口。

2 該書寫的域名寫法記得要加字首http或者其他方案(協定),看上面我說的如何判定是否同源就知道了。但自己測試的時候加端口卻無法正常傳回,并不知道是什麼原因

3 低版本的浏覽器并不支援該屬性,因為AJAX的出現也就在這幾年(我接觸的時候應該是09年,08年在上海的時候還沒搞過,不過可能已經有了),我自己測試的時候用的是IE8和firefox最新版,基本IE8下沒有辦法正常響應,而firefox下則可以。相容性問題有時會讓人比較郁悶。

4 用戶端請求似乎對浏覽器也有講究,網上些資料說的是IE8下則需要用另外一個對象進行跨域請求,但firefox下普通的AJAX調用就可以了。

是以總結如下,對于該屬性的跨域,在浏覽器的使用者五花八門的今天,為了保證相容性,則需要做大量測試和相容代碼書寫的工作。是以其實我并不推薦用這個方法來做跨域,除非以後大家都用上最新的浏覽器,相容性問題逐漸減少的時候(實際應該說網絡協定與代碼越來越規範的時候)采取考慮吧。

把我的測試代碼附帶如下

www.test1.com/index.html 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script language="javascript" src=">

<script language="javascript" src=">

<title>發起文檔</title>

</head> <body>

<div id="msg"></div>

<div id="go"></div>

<script language="javascript">

$("#go").click(function(){

    //這個是自己在jquery的架構ajax調用的基礎上書寫的js函數,如需測試可自己在jquery架構下自己手寫個

    //參數說明 第一個表示請求的URL 第二個POST傳遞的變量 第三個傳回結果的顯示在哪個對象中,這裡的調用是用html方式調用的

    //第四個 傳回後的回調函數

    JQ_CallAJAX("http://www.test2.com/index.php","",$("#msg"),"getReturn()");

});

function getReturn(){

    //書寫回調函數的相關代碼

}

</script>

</body>

</html>

www.test2.com/index.php

<?php

    header("Access-Control-Allow-Origin:http://www.test1.com");

    echo ("this page is ajax cross get");

?>

然後在本地電腦組態好相關的環境,修改host檔案增加www.test1.com和www.test2.com的解析指向,然後在firefox下運作就可以看到相關結果了。

二 使用不受同源政策影響的html元素進行跨域擷取

上文說到不受同源政策影響的元素有圖檔,樣式表檔案和js用戶端腳本檔案,這裡圖檔和樣式表我們都不需要,唯獨用到js腳本檔案。

另外一個技術就是服務端檔案可以生成發送至用戶端的任意檔案,比如圖檔。大家可以看到某些網站上的圖檔URL并不是.jpg結束,而是.asp或者.php字尾,其實就是有服務端檔案生成的,當然例如pdf檔案,doc檔案在服務端都可以為你生成,js檔案也不例外,隻要在服務端把contentType屬性設定好,然後檔案内容也規整好就成了,這個可以看下我之前些的一篇《WEB文檔提示下載下傳的研究》,這裡就不在詳細介紹

先上代碼再做解釋吧

www.test1.com/index.html 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script language="javascript" src=">

<title>發起文檔</title>

</head> <body>

<div id="msg"></div>

<div id="go"></div>

<script language="javascript">

$("#go").click(function(){

    //其中的callback的值為請求被接受後的回調函數名稱

    addScriptTag("http://www.test2.com/index.asp?callback=getReturn")

});

//回調傳回,其中data為回傳的json資料

function getReturn(data){

    if (data){

        alert(data.result);

    }else{

        alert("跨域調用不成功!");

    }

}

//自定義加載js包含标簽

function addScriptTag(src){

    var script = document.createElement('script');

    script.setAttribute("type","text/javascript");

    script.src = src;

    document.body.appendChild(script);

}

</script>

</body>

</html>

www.test2.com/index.asp

<%

 response.ContentType="application/json"

 callback=request.QueryString("callback")

 response.write(callback&"({""result"":""ok123""})")

%>

代碼說明,其實從test1發起的請求,實際是在body之間生成一個script标簽,該标簽的href=不同域下的一個檔案,雖然這裡是index.asp檔案,但如上所說,實際該服務端檔案生成的是一個js檔案,應為有ContentType="application/json"的這個頭部定義,是以可以放在script裡面進行調用,而調用的這個頁面隻要把相關的資料準備好就行了。是以之間就不存在同源政策的限制。

另外注意這裡有個callback的參數傳遞,這個是用于傳回資料的時候調用test1的相關js函數,否則直接傳回json資料将不會被響應。

三 同樣使用不受同源政策影響的js,在jquery架構中調用

其實在jquery現在的架構檔案中,(我用的是1.71)基本也支援這種方式的“跨域”,實際的調用類型叫JSONP,似乎類似于JSON,但具體怎麼解釋這個我就不太清楚了。

jquery的調用我也直接是用的ajax方法,隻不過該方法(對象)的屬性dataType設定為jsonp了,對于被請求頁面則不需要做任何修改,于是代碼我就羅列一個請求頁面的。

www.test1.com/index.html 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns=" http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script language="javascript" src=" >

<title>發起文檔</title>

</head> <body>

<div id="msg"></div>

<div id="go"></div>

<script language="javascript">

$("#go").click(function(){

    $.ajax({

        url:"http://www.test2.com/index.asp?callback=?",

        dataType:"jsonp",    //注意這裡必須寫jsonp

        jsonpCallback:"getReturn",

        success:function(data){

            alert(data.result);

        }

    });

});

//回調傳回,其中data為回傳的json資料

function getReturn(data){

    if (data){

        alert(data.result);

    }else{

        alert("跨域調用不成功!");

    }

}

</script>

</body>

</html>

其中ajax中的帶啊jsonpCallback為執行後的回調函數,但其中也有ajax自己的success的函數,實際測試下來兩個函數都會有相應,且回調函數的執行在success函數執行之前。如果把回調函數的聲明去掉,或者把success函數去掉,依然會響應,實際與是否連接配接到不同域下的檔案無關,而是與資料傳回過來的處理有關

由于時間有限,沒能測試通過該方法到一個實際的js檔案,然後該js檔案發起ajax請求與這個js同域下的一個服務端檔案結果是否可行,當然不可行也不妨礙使用,之際用ajax方法或者第二種都可以。隻不過用服務端檔案來準備js需要的資料而已,而不是通過不同域下的js再轉換。

是以綜合上面的叙述,AJAX跨域考慮浏覽器相容性的做法,要麼就直接寫js來動态添加script标簽進行異地js加載,或者直接用jquery架構來實作,兩者基本都能很好的執行。

最後參考網站貼一下,對人家勞動的尊重:http://www.cnblogs.com/chopper/archive/2012/03/24/2403945.html

繼續閱讀