JSON 与 JSONP

JSON (JavaScript Object Notation) is a lightweight data-interchange format. 即 JSON 是一种轻量级的数据交换格式。

 

1. JSON 的结构

JSON 构建于于两种(除去简单值)结构(JSON is built on two structures) 

对象名称(键) / 值对的集合(A collection of name / value pairs):

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

或者

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

说明:

一个对象以 { 开始,} 结束。每个名称(name)后跟一个 :

每个键值对之间使用 , 分隔

注意:JSON 对象属性里的非数字型键值必须要加双引号,不能不加或者加单引号

 

数组值(value)的有序集合(An array is an ordered collection of values)

类似于 JavaScript 中的数组:

["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

 
posted @ 2016-05-17 13:43  nemo20  阅读(3546)  评论(0编辑  收藏  举报
访客数:AmazingCounters.com
2016/05/17 起统计