JSON 與 JSONP

JSON (JavaScript Object Notation) is a lightweight data-interchange format. 即 JSON 是一種輕量級的數據交換格式。javascript

 

1. JSON 的結構

JSON 構建於於兩種(除去簡單值)結構(JSON is built on two structures) php

對象名稱(鍵) / 值對的集合(A collection of name / value pairs):css

{
  "key1": "value1",
  "key2": "value2"    
}

或者html

{
  "name": "dee",
  "age": 28,
  "stack": {
       "frontend": "JavaScript",
       "backend": "php" 
    }      
}

說明:java

一個對象以 { 開始,} 結束。每一個名稱(name)後跟一個 :node

每一個鍵值對之間使用 , 分隔mysql

注意:JSON 對象屬性裏的非數字型鍵值必需要加雙引號,不能不加或者加單引號jquery

 

數組值(value)的有序集合(An array is an ordered collection of values)linux

相似於 JavaScript 中的數組:ajax

["dee", 28, "developer"]

 

把對象和數組結合起來,能夠構建更復雜的數據集合:

[
    {
        "title": "PHP Cookbook",
        "authors": [
            "Sklar",
            "Adam"
        ],
        "edition": 3
    },
    {
        "title": "Modern PHP",
        "authors": "Lockhart"
    }
]

說明:

一個數組以 [ 開始,] 結束。值之間使用 , 分隔。

值(value)能夠是字符串(string)、數值(number)、truefalse、 null、對象(object)或者數組(array)。這些結構能夠嵌套。字符串類型的值必須雙引號括起來

 

2. JavaScript 解析和序列化 JSON

解析:把 JSON 字符串轉換爲 JavaScript 對象

序列化: 把Javascript 對象轉換爲 JSON 字符串

JSON 流行的緣由除了與 JavaScript 相似的語法,還有一個緣由就是能夠把 JSON 數據結構解析爲 JavaScript 對象。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script>
        // JavaScript 對象
        var book = [
        {
            "title": "PHP Cookbook",
            "authors": [
                "Sklar",
                "Adam"
            ],
            "edition": 3
        },
        {
            "title": "Modern PHP",
            "authors": "Lockhart"
        }
        ];
        console.log(book);

        // 把 JavaScript 序列化爲 JSON 字符串 
        var jsonText = JSON.stringify(book);
        console.log(jsonText);

        // 把 JSON 字符串解析爲 JavaScript 對象
        var bookCopy = JSON.parse(jsonText);
        console.log(bookCopy);

        // 舊版本瀏覽器使用 eval() 函數解析 JSON 字符串爲 JavaScript 對象
        // 結果和使用 JSON.parse() 同樣
        var bookCopy_ = eval(jsonText);
        console.log(bookCopy_);

    </script>
</head>
<body>
    
</body>
</html>

結果以下(FireFox 44.0.2):

 

 

3. jQuery 和 Ajax 獲取與發送 JSON 

① 獲取 JSON 數據

客戶端 json.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://lib.sinaapp.com/js/jquery/1.9.1/jquery-1.9.1.min.js"></script>
    <script>
        $.ajax({
            url: "index.php",
            type: "post",
            dataType: "json", // 代表接收的返回值是 JSON 格式的 
            data: {
                "name": "dee",
                "job": "developer"
            },
            success: function(data) {
                console.log(data);
                $.each(data.stack, function(name, value) {
                    console.log(value);
                });
            },
            error: function() {
                console.log("error");
            }
        });
    </script>
</head>
<body>
    
</body>
</html>

 

服務器端 index.php

<?php

if(isset($_POST['name']) && $_POST['name'] == 'dee') {

    $data['code'] = 200;
    $data['message'] = 'success';
    $data['stack']['frontend'] = ['html', 'css', 'javascript'];
    $data['stack']['backend'] = ['php', 'mysql', 'nosql', 'linux', 'node.js'];

    header('content-type:text/json');
    exit(json_encode($data));
}

輸出:

 

說明:在服務端寫 API 接口的時候,若是是以 JSON 做爲數據交換格式,要加上:

header('content-type:text/json');

此時接收到數據的客戶端接受到的數據已是一個 JavaScript 對象,不須要再作任何的解析工做,jQuery 已經自動把 JSON 字符串轉換爲了 JSON 對象。

 

在客戶端接收以 JSON 爲數據交換格式的 API 接口返回的數據時,也要加上:

dataType: "json",

代表接收的返回值是 JSON 格式的。

 

 

② 發送 JSON 數據

客戶端 json2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://lib.sinaapp.com/js/jquery/1.9.1/jquery-1.9.1.min.js"></script>
</head>
<body>
    <form>
        <table>
            <tr>
                <td>name:</td>
                <td><input name="name" type="text"></td>
            </tr>
            <tr>
                <td>job:</td>
                <td><input type="text" name="job"></td>
            </tr>
            <tr>
                <td clospan="2">
                    <input type="button" id="submit" value="submit">
                </td>
            </tr>
        </table>
    </form>
</body>
<script>
    $('#submit').click(function(){
        $.ajax({
            url: "index2.php",
            type: "post",
            contentType: "application/json",
            data: JSON.stringify($('form').serializeArray()),
            dataType: "json",
            success: function(data) {
                console.log(data);
            },
            error: function() {
                console.log("error");
            }
        });
    });
</script>
</html>

說明:

把 contentType 設置爲 application/json,即 JSON 的 MIME 類型,發送的數據的類型是 JSON 字符串,後端使用 PHP 處理請求時,須要從 php://input 裏得到原始輸入流,再 json_decode 成對象;

serializeArray 會生成一個數組對象,每個元素都表示一個表單 input 元素的屬性的對象。

 

服務器端:

index2.php

<?php

$data = file_get_contents('php://input'); // 從 php://input 裏得到原始輸入流
$data = json_decode($data, true);

$response['code'] = 200;
$response['message'] = 'success';
$response['data'] = $data;

header('content-type:text/json');
exit(json_encode($response));

 

POST 數據:

 

響應:

 

4. JSONP 

同源策略:

瀏覽器有一種同源策略(Same origin policy),同源是指域名、協議、端口相同,當瀏覽器執行一個腳本(例如 JavaScript)的時候會檢查這個腳本是否同源,不然不會執行該腳本,由於瀏覽器認爲來自同站點的資源是安全的。在瀏覽器中,<script>、<img>、<iframe>、<link> 等標籤均可以加載跨域資源,而不受同源限制,但瀏覽器限制了JavaScript 的權限使其不能讀、寫加載的內容。另外同源策略只對網頁的 HTML 文檔作了限制,對加載的其餘靜態資源如 JavaScript、css、圖片等仍然認爲屬於同源。(參考:

同源策略同源策略和跨域訪問)。

例如,客戶端經過 XMLHttpRequest 跨域與服務端交互,訪問 http://www.site1.com/ajaxjson.html

ajaxjson.html:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://lib.sinaapp.com/js/jquery/1.9.1/jquery-1.9.1.min.js"></script>
</head>
<body>
    <script>
    $.ajax({
        url: "http://www.site2.com/index.php",
        type: "post",
        success: function(data) {
            console.log(data);
        },
        error: function() {
            console.log("error");
        }
    });
    </script>
</body>
</html>

因爲請求的域是 www.site2.com,屬於跨域請求,因此在訪問 http://www.site1.com/ajaxjson.html 時,瀏覽器會給出提示:

 

火狐下:

 

Chrome 下:

 

要解決這類跨域問題,可使用 JSONP 做爲解決方案。 

 

JSONP:JSON with padding(參數式 JSON),包含在函數調用中的 JSON。如:

callback({"name": "dee"});

 

JSONP 利用在頁面中建立 <script> 節點的方法向不一樣域提交 HTTP請求。例,兩個域 http://www.site1.com 和 http://www.site2.com 實現跨域交互

客戶端 site/index.html:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>site1</title>
</head>
<body>
    <p id="response"></p>
</body>
<script>
    function jsonCallback(data) {
        document.getElementById("response").innerText = data.name;
        console.log(data);
    }
</script>
<script src="http://www.site2.com/index.php?callback=jsonCallback "></script>
</html>

 

服務端 site2/index.php:

<?php

$callback = isset($_GET['callback']) ? $_GET['callback'] : '';

echo $callback.'({"name": "emperor"})';

 

訪問 http://www.site1.com/index.html,返回:

說明:

其中 jsonCallback 是客戶端註冊的,獲取跨域服務器上的 json 數據後,回調的函數。http://www.site2.com/index.php?callback=jsonpCallback 這個 url 是跨域服務器取 json 數據的接口,參數爲回調函數的名字,返回的格式爲:jsonpCallback({"name": "dee"})

簡述原理與過程:首先在客戶端註冊一個 callback, 而後把 callback 的名字傳給服務器。此時,服務器先生成 json 數據。 而後以 javascript 語法的方式,生成一個function , function 名字就是傳遞上來的參數 jsonp 。最後將 json 數據直接以入參的方式,放置到 function 中,這樣就生成了一段 js 語法的文檔,返回給客戶端。

客戶端瀏覽器,解析 script 標籤,並執行返回的 javascript 文檔,此時數據做爲參數,傳入到了客戶端預先定義好的 callback 函數裏。(動態執行回調函數)

(參考 JSONP跨域的原理解析) 

 

一樣的,jQuery 也對 JSONP 進行了封裝,可使用 jQuery 進行跨域請求:

site1/ajaxjsonp.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://lib.sinaapp.com/js/jquery/1.9.1/jquery-1.9.1.min.js"></script>
</head>
<body>
    <script>
    $.ajax({
        url: "http://www.site2.com",
        dataType: "jsonp", // Tell jQuery we're expecting JSONP
        jsonp: "callback", // 回調參數的名稱 The name of the callback parameter
        success: function(data) {
            console.log(data);
        },
        error: function() {
            console.log("error");
        }
    });
    </script>
</body>
</html>

 

瀏覽器此時不會再提示已組織跨源請求而返回了服務端的數據:

 

 

JSONP 的優勢是:它不像 XMLHttpRequest 對象實現的 Ajax 請求那樣受到同源策略的限制;它的兼容性更好,在更加古老的瀏覽器中均可以運行,不須要 XMLHttpRequest 或 ActiveX 的支持;而且在請求完畢後能夠經過調用 callback 的方式回傳結果。

JSONP 的缺點則是:它只支持 GET 請求而不支持 POST 等其它類型的 HTTP 請求。(參考 JSONP跨域的原理解析

 

JSONP的最基本的原理是:動態添加一個 <script> 標籤,而 script 標籤的 src 屬性是沒有跨域的限制的。這樣說來,這種跨域方式其實與 ajax XmlHttpRequest 協議無關了。更多原理能夠參考 JSONP跨域的原理解析

若是設爲dataType: 'jsonp',這個$.ajax方法就和ajax XmlHttpRequest沒什麼關係了,取而代之的則是JSONP協議。JSONP是一個非官方的協議,它容許在服務器端集成Script tags返回至客戶端,經過javascript callback的形式實現跨域訪問。

JSONP即JSON with Padding。因爲同源策略的限制,XmlHttpRequest只容許請求當前源(域名、協議、端口)的資源。若是要進行跨域請求, 咱們能夠經過使用html的script標記來進行跨域請求,並在響應中返回要執行的script代碼,其中能夠直接使用JSON傳遞javascript對象。 這種跨域的通信方式稱爲JSONP。

jsonCallback 函數jsonp1236827957501(....):是瀏覽器客戶端註冊的,獲取跨域服務器上的json數據後,回調的函數

Jsonp的執行過程以下:

首先在客戶端註冊一個callback (如:'jsoncallback'), 而後把callback的名字(如:jsonp1236827957501)傳給服務器。注意:服務端獲得callback的數值後,要用jsonp1236827957501(......)把將要輸出的json內容包括起來,此時,服務器生成 json 數據才能被客戶端正確接收。

而後以 javascript 語法的方式,生成一個function, function 名字就是傳遞上來的參數 'jsoncallback'的值 jsonp1236827957501 .

最後將 json 數據直接以入參的方式,放置到 function 中,這樣就生成了一段 js 語法的文檔,返回給客戶端。

客戶端瀏覽器,解析script標籤,並執行返回的 javascript 文檔,此時javascript文檔數據,做爲參數, 傳入到了客戶端預先定義好的 callback 函數(如上例中jquery $.ajax()方法封裝的的success: function (json))裏。

能夠說jsonp的方式原理上和<script src="http://跨域/...xx.js"></script>是一致的(qq空間就是大量採用這種方式來實現跨域數據交換的)。JSONP是一種腳本注入(Script Injection)行爲,因此有必定的安全隱患。

 

 

5.其餘跨域解決方案

CORS(跨域資源共享,Cross-Origin Resource Sharing)是 W3 的一項機制(https://www.w3.org/TR/cors/),跨源資源共享標準經過新增一系列 HTTP 頭,讓服務器能聲明哪些來源能夠經過瀏覽器訪問該服務器上的資源。

例:

在 site2/cors.php 中添加 header:

header('Access-Control-Allow-Origin: *');

* 表示容許任何域向服務端提交請求

服務端代碼:

<?php

header('Access-Control-Allow-Origin: *');

$response['code'] = 200;
$response['message'] = 'success';
$response['name'] = 'dee';

header('Content-type: text/json');
exit(json_encode($response));

 

客戶端 site1/cors.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://lib.sinaapp.com/js/jquery/1.9.1/jquery-1.9.1.min.js"></script>
</head>
<body>
    <script>
    $.ajax({
        url: "http://www.site2.com/cors.php",
        type: "post",
        dataType: "json",
        success: function(data) {
            console.log(data);
        },
        error: function() {
            console.log("error");
        }
    });
    </script>
</body>
</html>

瀏覽器 console 輸出:

 

當把 header 改成:

header('Access-Control-Allow-Origin: http://www.site3.com');

即只容許來自 http://www.site3.com 的 AJAX 請求

 

再次訪問 http://www.site1.com,因爲設置了只容許 site3 的 AJAX 請求,因此瀏覽器輸出:

 

 

把 header 再次改成:

header('Access-Control-Allow-Origin: http://www.site1.com');

輸出:

 

參考:

json.org

《JavaScript高級程序設計》3nd

《精通jQuery》2nd

https://learn.jquery.com/ajax/working-with-jsonp/

JSONP跨域的原理解析

HTTP訪問控制(CORS)

AJAX POST&跨域 解決方案 - CORS

相關文章
相關標籤/搜索