JSON和JSONP的区别

先前的概念中对JSON还是比较熟悉JSONP不是特别的清楚整理完相关知识发现才豁然开朗简单的说JSON是一种数据交换格式而JSONP是 一种非官方跨域数据交互协议。JSON暗号”,JSONP则是接头方式一个是描述信息的格式一个是信息传递双方约定的方法

什么是JSON

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式 易于人阅读和编写同时也易于机器解析和生成 它基于JavaScript Programming LanguageStandard ECMA-262 3rd Edition – December 1999的一个子集 JSON采用完全独立于语言的文本格式但是也使用了类似于C语言家族的习惯包括C, C++, C#, Java, JavaScript, Perl, Python)。 这些特性使JSON成为理想的数据交换语言

JSON建构于两种结构

  • 名称/对的集合(A collection of name/value pairs)。不同的语言中它被理解为对象(object),纪录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 (associative array)。
  • 值的有序列表(An ordered list of values)。在大部分语言中它被理解为数组(array)。

这些都是常见的数据结构事实上大部分现代计算机语言都以某种形式支持它们这使得一种数据格式在同样基于这些结构的编程语言之间交换成为可能

JSON具有以下这些形式

对象是一个无序的“‘名称/集合一个对象以“{”(左括号开始,“}”(右括号结束每个名称后跟一个“:”(冒号);“‘名称/之间使用“,”(逗号分隔

数组是值(value)的有序集合一个数组以“[”(左中括号开始,“]”(右中括号结束值之间使用“,”(逗号分隔

value可以是双引号括起来的字符串string)、数值(number)、truefalse、 null对象(object)或者数组(array)。这些结构可以嵌套

字符串string是由双引号包围的任意数量Unicode字符的集合使用反斜线转义一个字符(character)即一个单独的字符串(character string)。

字符串stringC或者Java的字符串非常相似

数值number也与C或者Java的数值非常相似除去未曾使用的八进制与十六进制格式除去一些编码细节

空白可以加入到任何符号之间 以下描述了完整的语言

JSON的优点

  1. 基于纯文本跨平台传递极其简单
  2. Javascript原生支持后台语言几乎全部支持
  3. 轻量级数据格式占用字符数量极少特别适合互联网传递
  4. 可读性较强虽然比不上XML那么一目了然但在合理的依次缩进之后还是很容易识别的
  5. 容易编写和解析当然前提是你要知道数据结构
// 描述一个人
 
var person = {
    "Name": "Bob",
    "Age": 32,
    "Company": "IBM",
    "Engineer": true
}
 
// 获取这个人的信息
 
var personAge = person.Age;
 
// 描述几个人
 
var members = [
    {
        "Name": "Bob",
        "Age": 32,
        "Company": "IBM",
        "Engineer": true
    },
    {
        "Name": "John",
        "Age": 20,
        "Company": "Oracle",
        "Engineer": false
    },
    {
        "Name": "Henry",
        "Age": 45,
        "Company": "Microsoft",
        "Engineer": false
    }
]
 
// 读取其中John的公司名称
 
var johnsCompany = members[1].Company;
 
// 描述一次会议
 
var conference = {
    "Conference": "Future Marketing",
    "Date": "2012-6-1",
    "Address": "Beijing",
    "Members":
    [
        {
            "Name": "Bob",
            "Age": 32,
            "Company": "IBM",
            "Engineer": true
        },
        {
            "Name": "John",
            "Age": 20,
            "Company": "Oracle",
            "Engineer": false
        },
        {
            "Name": "Henry",
            "Age": 45,
            "Company": "Microsoft",
            "Engineer": false
        }
    ]
}
 
// 读取参会者Henry是否工程师
 
var henryIsAnEngineer = conference.Members[2].Engineer;

什么是JSONP?

JSONP(JSON with Padding)是资料格式 JSON 的一种使用模式”,可以让网页从别的网域要资料由于同源策略一般来说位于 server1.example.com 的网页无法与不是 server1.example.com 的服务器沟通而 HTML 的 <script> 元素是一个例外利用<script> 元素的这个开放策略网页可以得到从其他来源动态产生的 JSON 资料而这种使用模式就是所谓的 JSONP。JSONP 抓到的资料并不是 JSON,而是任意的 JavaScript,JavaScript 直译器执行而不是用 JSON 解析器解析

为了理解这种模式的原理先想像有一个回传 JSON 文件的 URL,JavaScript 程式可以用 XMLHttpRequest 跟这个 URL 要资料假设我们的 URLhttp://server2.example.com/RetrieveUser?UserId=xxx 。假设小明的 UserId1823,且当浏览器透过 URL 传小明的 UserId,也就是抓取http://server2.example.com/RetrieveUser?UserId=1823 ,得到

{"Name": "小明", "Id" : 1823, "Rank": 7}

这个 JSON 资料可能是依据传过去 URL 的查询参数动态产生的

这个时候把 <script> 元素的 src 属性设成一个回传 JSONURL 是可以想像的这也代表从 HTML 页面透过 script 元素抓取 JSON 是可能的

然而一份 JSON 文件并不是一个 JavaScript 程式为了让浏览器可以在 <script> 元素执行srcURL 回传的必须是可执行的 JavaScript。JSONP 的使用模式里URL 回传的是由函数呼叫包起来的动态生成 JSON,这就是JSONP填充(padding)”或是前辍(prefix)”的由来

惯例上浏览器提供回调函数的名称当作送至服务器的请求中命名查询参数的一部份例如

<script type="text/javascript" src="http://server2.example.com/RetrieveUser?UserId=1823&jsonp=parseResponse"> </script>

服务器会在传给浏览器前将 JSON 数据填充到回调函数(parseResponse)浏览器得到的回应已不是单纯的资料叙述而是一个脚本在本例中浏览器得到的是

parseResponse({"Name": "Cheeso", "Id" : 1823, "Rank": 7})

虽然这个填充前辍)“通常是浏览器执行背景中定义的某个回调函数它也可以是变量赋值、if 叙述或者是其他 JavaScript 叙述。JSONP 要求也就是使用 JSONP 模式的请求的回应不是 JSON 也不被当作 JSON 解析——回传内容可以是任意的运算式甚至不需要有任何的 JSON,不过惯例上填充部份还是会触发函数调用的一小段 JavaScript 片段而这个函数呼叫是作用在 JSON 格式的资料上的

另一种说法—典型的 JSONP 就是把既有的 JSON API 用函数呼叫包起来以达到跨域存取的解法为了要启动一个 JSONP 呼叫或者说使用这个模式),你需要一个 script 元素因此浏览器必须为每一个 JSONP 要求加或是重用一个新的有所需 src 值的 <script> 元素到 HTML DOM 里—或者说是注入这个元素浏览器执行该元素抓取 src 里的 URL,并执行回传的 JSON。也因为这样,JSON 被称作是一种让使用者利用 script 元素注入的方式绕开同源策略的方法

使用远端网站的 script 标签会让远端网站得以注入任何的内容至网站里如果远端的网站有 JavaScript 注入漏洞原来的网站也会受到影响.现在有一个正在进行计划在定义所谓的 JSON-P 严格安全子集使浏览器可以对 MIME 类别是“application/json-p”请求做强制处理如果回应不能被解析为严格的 JSON-P,浏览器可以丢出一个错误或忽略整个回应

粗略的 JSONP 部署很容易受到跨网站的伪造要求(CSRF/XSRF)的攻击因为 HTML <script> 标签在浏览器里不遵守同源策略恶意网页可以要求并取得属于其他网站的 JSON 资料当使用者正登入那个其他网站时上述状况使得该恶意网站得以在恶意网站的环境下操作该 JSON 资料可能泄漏使用者的密码或是其他敏感资料

只有在该 JSON 资料含有不该泄漏给第三方的隐密资料且服务器仅靠浏览器的同源策略阻挡不正常要求的时候这才会是问题若服务器自己决定要求的专有性并只在要求正常的情况下输出资料则没有问题只靠 Cookie 并不够决定要求是合法的这很容易受到跨网站的伪造要求攻击

JSONP的客户端具体实现

下面来说明一下jsonp在客户端的实现

1、我们知道哪怕跨域js文件中的代码当然指符合web脚本安全策略的),web页面也是可以无条件执行的

远程服务器remoteserver.com根目录下有个remote.js文件代码如下

alert('我是远程文件')

本地服务器localserver.com下有个jsonp.html页面代码如下

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
</head>
<body>
 
</body>
</html>

毫无疑问页面将会弹出一个提示窗体显示跨域调用成功

2、现在我们在jsonp.html页面定义一个函数然后在远程remote.js中传入数据进行调用

jsonp.html页面代码如下

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
var localHandler = function(data){
alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result);
};
</script>
<script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
</head>
<body>
 
</body>
</html>

remote.js文件代码如下

localHandler({“result”:”我是远程js带来的数据”}); 运行之后查看结果页面成功弹出提示窗口显示本地函数被跨域的远程js调用成功并且还接收到了远程js带来的数据很欣喜跨域远程获取数据的目的基本实现了但是又一个问题出现了我怎么让远程js知道它应该调用的本地函数叫什么名字呢毕竟是jsonp的服务者都要面对很多服务对象而这些服务对象各自的本地函数都不相同啊我们接着往下看

聪明的开发者很容易想到只要服务端提供的js脚本是动态生成的就行了呗这样调用者可以传一个参数过去告诉服务端我想要一段调用XXX函数的js代码请你返回给我”,于是服务器就可以按照客户端的需求来生成js脚本并响应了

jsonp.html页面的代码

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
// 得到航班信息查询结果后的回调函数
var flightHandler = function(data){
alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');
};
// 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";
// 创建script标签,设置其属性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script标签加入head,此时调用开始
document.getElementsByTagName('head')[0].appendChild(script);
</script>
</head>
<body>
 
</body>
</html>

这次的代码变化比较大不再直接把远程js文件写死而是编码实现动态查询而这也正是jsonp客户端实现的核心部分本例中的重点也就在于如何完成jsonp调用的全过程

我们看到调用的url中传递了一个code参数告诉服务器我要查的是CA1998次航班的信息callback参数则告诉服务器我的本地回调函数叫做flightHandler,所以请把查询结果传入这个函数中进行调用

OK,服务器很聪明这个叫做flightResult.aspx的页面生成了一段这样的代码提供给jsonp.html(服务端的实现这里就不演示了与你选用的语言无关说到底就是拼接字符串):

flightHandler({
"code": "CA1998",
"price": 1780,
"tickets": 5
});

我们看到传递给flightHandler函数的是一个json,它描述了航班的基本信息运行一下页面成功弹出提示窗口,jsonp的执行全过程顺利完成

4、到这里为止的话相信你已经能够理解jsonp的客户端实现原理了吧剩下的就是如何把代码封装一下以便于与用户界面交互从而实现多次和重复调用

jQueryJSONP的实现

我们依然沿用上面那个航班信息查询的例子假定返回jsonp结果不变

<!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>
<title>Untitled Page</title>
<script type="text/javascript" src=jquery.min.js"></script>
<script type="text/javascript">
jQuery(document).ready(function(){
 
$.ajax({
type: "get",
async: false,
url: "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998",
dataType: "jsonp",
jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据
success: function(json){
alert('您查询到航班信息:票价: ' + json.price + ' 元,余票: ' + json.tickets + ' 张。');
},
error: function(){
alert('fail');
}
});
});
</script>
</head>
<body>
</body>
</html>

为什么我这次没有写flightHandler这个函数呢而且竟然也运行成功了这就是jQuery的功劳了,jquery在处理jsonp类型的ajax时自动帮你生成回调函数并把数据取出来供success属性方法来调用

ajaxjsonp的异同

  1. ajaxjsonp这两种技术在调用方式上看起来很像目的也一样都是请求一个url,然后把服务器返回的数据进行处理因此jqueryext等框架都把jsonp作为ajax的一种形式进行了封装
  2. ajaxjsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本
  3. 所以说其实ajaxjsonp的区别不在于是否跨域,ajax通过服务端代理一样可以实现跨域,jsonp本身也不排斥同域的数据的获取
  4. 还有就是,jsonp是一种方式或者说非强制性协议如同ajax一样它也不一定非要用json格式来传递数据如果你愿意字符串都行只不过这样不利于用jsonp提供公开服务

总而言之,jsonp不是ajax的一个特例哪怕jquery等巨头把jsonp封装进了ajax,也不能改变着一点

posted @ 2015-06-19 15:11  Napoléon  阅读(5549)  评论(0编辑  收藏  举报