由于同源政策的限制,
Ajax
不允許跨域通路,但是像
<script>
、
<link>
、
<img>
、
<iframe>
這些标簽是允許跨域的,但你并不能修改這些資源,比如
iframe
裡的内容,更準确的來說這些标簽可以擷取資源。
JSONP
是
JSON with padding
(填充式
JSON
)。
JSONP
利用了
HTML
中
<script>
标簽可以跨域的原理,利用
<script>
标簽向伺服器請求一段js代碼,然後執行這一段JS代碼,實作跨域。
首先,要了解
<script>
标簽上發生了什麼。在編碼中我們經常會通過
<script>
引用cdn等處于其它域名下的靜态資源,毫無疑問是可以加載成功的。其實
<script>
标簽的實質就是往
<script>
标簽裡的src發起一個請求,擷取到JS代碼,然後把代碼放到目前執行。實際上裡面的.js字尾隻是我們習慣的用法,無論是什麼檔案,都會把裡面的内容下載下傳下來當作JS在目前來執行。是以,即使請求對的是
.txt
檔案,也會把
.txt
裡面的内容下載下傳下來當作JS在目前來執行。是以,無論
<script>
标簽裡可以是任何東西,隻要是可以通路到并且得到資料的一個接口就可以了,都會把傳回的内容當作JS在目前運作。
下面我們來了解以下Ajax是怎麼利用
<script>
标簽來進行跨域通路的。
首先要說一下,
JSONP
是需要前後端的配合的,很多人之是以對對
JSONP
懵懵懂懂,從我自身的感覺來說,很多是因為不了解後端是怎麼與前端配合實作
JSONP
的。
前端代碼:
**HTML**
<button class="getData">擷取資料</button>
**JavaScript**
var btn=document.getElementById("#getData");
btn.addEventListener('click', function(){
var script = document.createElement('script');
script.src = 'http://www.wuxiaozhou.com/getData?callback=getData';
document.head.appendChild(script);
document.head.removeChild(script);
})
function getData(data){
document.write(data);
}
後端代碼(後端語言有很多種,可能不同語言的代碼會不一樣):
app.get('/getData', function(req, res){
var data = ["海賊王","火影忍者","灌籃高手","名偵探柯南"];
var cb = req.query.callback;
if(cb){
res.send(cb + '('+ JSON.stringify(data) + ')');
}else{
res.send(data);
}
})
前端代碼裡,比如代碼頁面所在的域名是
http://www.xiaozhou.com
我給個按鈕綁定了一個事件,點選按鈕會向為
http://www.wuxiaozhou.com/getData
擷取資料。因為兩者的主機名不一樣,是以這個是跨域通路。
其實是先建立一個
<script>
标簽,然後把
<script>
标簽的src屬性設定為擷取資料的接口加一個鍵值對
http://www.wuxiaozhou.com/getData?callback=getData
,再把
<script>
标簽放到頁碼的
<head>
标簽裡面。當點選了按鈕後,上面的操作就會進行,就會向
http://www.wuxiaozhou.com/getData
發起請求。
也許你會有疑問,為什麼需要在請求的接口後面加上一個鍵值對?下面就為你解答。
後端接收到請求後,找到
callback
的值,然後把
callback
的值加上用括号括起來并轉化成字元串的需要傳回的資料傳回來。在我的這個例子中,
callback
的值就是
getData
,傳回給用戶端的就是
getData('["海賊王","火影忍者","灌籃高手","名偵探柯南"]')
。
可能你也注意到後端語言裡有一個判斷,如果判斷到有
callback
的值存在,那就說明是一個跨域的請求,是按照上面我們說的方式發起的,用
if
後面的方式傳回資料;因為同域請求的話請求裡是沒有後面的那一個鍵值對的,是以背景判斷沒有這個
callback
的值,就會将資料直接傳回,也就是
else
後面的方式。這裡是為了同時可以處理同源請求和跨域的
JSONP
請求。
前端頁面接收到後端傳回對的資料後,就會将傳回的資料當作JS去執行,在我們這個例子中就是:
你也注意到我們在前端代碼裡聲明了一個與
callback
的值一樣的函數,在我們這個例子就是:
function getData(data){
document.write(data);
}
是以就會執行這個函數,參數是
'["海賊王","火影忍者","灌籃高手","名偵探柯南"]'
。這個時侯相當于資料被當作參數傳進了這個函數了,這個函數就是資料的處理函數,你也可以對傳回的字元串資料進行其它的各種各樣的操作。在我的這個例子中就會把
'["海賊王","火影忍者","灌籃高手","名偵探柯南"]'
顯示在頁面上。
前端跟後端要協調好的就是在發送請求的參數名要一緻,比如我這個例子的參數名是
callback
,後端要查詢的參數名也要是
callback
,否則的話後端就找不到前端發送的參數。沒有特殊說明,一般約定俗成都是
callback
吧。