1. 程式人生 > >js和jquery使用jsonp解決跨域

js和jquery使用jsonp解決跨域

跨域

理解跨域首先必須要了解同源策略。同源策略是瀏覽器上為安全性考慮實施的非常重要的安全策略。

何謂同源:

URL由協議、域名、埠和路徑組成,如果兩個URL的協議、域名和埠相同,則表示他們同源。

由於同源策略,而且隨著網際網路的發展,“同源策略”越來越嚴格。目前,如果非同源,共有三種行為收到限制。

  • Cookie、LocalStorage 和 IndexDB 無法讀取
  • DOM 無法獲得
  • Ajax 請求不能傳送

同源政策規定,AJAX 請求只能發給同源的網址,否則就報錯。
除了架設伺服器代理(瀏覽器請求同源伺服器,再由後者請求外部服務),有三種方法規避這個限制。

  • JSONP
  • WebSocket
  • CORS

文字將介紹分別 js 和 jquery 來使用 jsonp。
後面所有的例子都是執行在伺服器環境下的。

好的,用一個簡單的例子來引入問題,先看下面的demo:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script src="http://apps.bdimg.com/libs/jquery/1.10.2/jquery.js"
>
</script> </head> <body> <script> $(document).ready(function(){ document.write('載入baidu靜態資源公共庫'); }); </script> </body> </html>

這是我們開篇用的那個例子,在伺服器環境下,能夠正常執行,頁面會有顯示,也就是說我們成功的載入了來自於百度靜態資源公共庫中的 jquery
對的,我們跨域了嗎?我們跨域了嗎?
在這個例子中,其實我們已經跨域了,因為
<script src="http://apps.bdimg.com/libs/jquery/1.10.2/jquery.js"></script>


這個 jquery 來自於百度靜態資源公開庫,百度的這個域名肯定與我們本地的不同,但是我們卻成功的載入了這段 jquery 程式碼到我們的頁面上。這是為什麼呢?

瀏覽器會進行同源檢查(看清楚,這裡是瀏覽器會進行同源檢測。),這導致了跨域問題,然而這個跨域檢查還有一個例外那就是 HTML 的 <script> 標記;我們經常使用 <script> 的 src 屬性,指令碼靜態資源放在獨立域名下或者來自其它站點的時候這裡是一個 url;這個 url 響應的結果可以有很多種,比如 JSON,返回的 json 值成為 <Script> 標籤的 src 屬性值.這種屬性值變化並不會引起頁面的影響。 按照慣例,瀏覽器在 url 的查詢字串中提供一個引數,這個引數將作為結果的字首一起返回到瀏覽器;

Web頁面上呼叫 js 檔案時則不受是否跨域的影響(不僅如此,我們還發現凡是擁有“src”這個屬性的標籤都擁有跨域的能力,比如 <script><img><iframe>
在瀏覽器中,<script<img><iframe><link> 等標籤都可以載入跨域資源,但瀏覽器限制了 JavaScript 的許可權使其不能讀、寫載入的內容。
另外同源策略只對網頁的 HTML 文件做了限制,對載入的其他靜態資源如 javascript 、 css、圖片等仍然認為屬於同源。

這就是為什麼我們可以使用 <script src="http://apps.bdimg.com/libs/jquery/1.10.2/jquery.js"></script> 來載入跨域的資源的原因。

OK,下面我們來看一個 沒跨域的情況下 使用 jquery 發出請求載入資料的例子:
後臺程式碼(result.php):

<?php
    if($_GET) {
        $str = '{"id":"get"}'; 
        echo $str;
    }   
    if($_POST){
        $str = '{"id":"post"}'; 
        echo $str;
    }
?>

我們在 demo.html 中使用 jquery 分別使用 GET 和 POST 方式 向 result.php 發出請求:
demo.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <a href="#" id="jq-get">jq-get</a>
    <br>
    <a href="#" id="jq-post">jq-post</a>
    <br>
    <p>後臺返回的資料如下:</p>
    <div id="result"></div>
    <script src="http://apps.bdimg.com/libs/jquery/1.10.2/jquery.js"></script>
    <script>
        $('#jq-get').click(function(){
            $.ajax({
                type: "GET",
                url: "http://10.2.229.89:8082/test-jq-jsonp/jsonp.php",
                data: {
                    "a": 1
                },
                dataType: "json",
                success: function(data){
                    $('#result').html(data.id);
                }
            });
        });

        $('#jq-post').click(function(){
            $.ajax({
                type: "POST",
                url: "http://10.2.229.89:8082/test-jq-jsonp/jsonp.php",
                data: {
                    "a": 1
                },
                dataType: "json",
                success: function(data){
                    $('#result').html(data.id);
                }
            });
        }); 
    </script>
</body>
</html>

demo.html 與 result.php 均在 8082 埠下,不存在跨域的情況,
執行效果圖如下:

可見,在沒有跨域的情況下,是很好用的。

下面,我們將 demo.html 移到本機的另一個埠(port: 3000)下,那麼 demo.html 與 result.php 就存在了跨域的情況:

使用 jsonp 來解決這個問題

$('#jq-get').click(function(){
    $.ajax({
        type: "GET",
        url: "http://10.2.229.89:8082/test-jq-jsonp/jsonp.php",
        data: {
            "a": 1
        },
        dataType: "jsonp",
        success: function(data){
            $('#result').html(data.id);
        }
    });
});

result.php也有一點改動

if($_GET) {
    $jsonp = $_REQUEST["callback"]; 
    $str = '{"id":"get"}'; 
    $str = $jsonp . "(" .$str.")"; 
    echo $str;
}

關於 jsonp 為什麼要這麼寫,結合文章開頭說的 <script> 標籤擁有跨域的能力以及之前的一篇文章 關於JSON和JSONP的詳解

那麼,我們 POST 方式 能不能這樣用呢?

$('#jq-post').click(function(){
    $.ajax({
        type: "POST",
        url: "http://10.2.229.89:8082/test-jq-jsonp/jsonp.php",
        data: {
            "a": 1
        },
        dataType: "jsonp",
        success: function(data){
            $('#result').html(data.id);
        }
    });
});

我們測試以後可以發現,雖然我們設定的是 POST 方式,但是呼叫的還是 GET 方法。那麼 POST 方式跨域要如何解決呢?

我們需要在後臺程式碼(result.php)檔案頭加入請求頭和跨域許可權:

<?php
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Methods:POST');

    if($_POST){
        $str = '{"id":"post"}';
        echo $str;
    }   
?>