我知道JSONP是带填充的JSON。

我了解JSON是什么,以及如何使用jQuery.getJSON()。但是,在介绍JSONP时,我不理解回调的概念。

谁能给我解释一下这是怎么回事?


假设你有一些URL,给你JSON数据,比如:

{'field': 'value'}

...你有一个类似的URL,除了它使用JSONP,你传递回调函数名称'myCallback'(通常通过给它一个名为'callback'的查询参数,例如http://example.com/dataSource?callback=myCallback)。然后它会返回:

myCallback({'field':'value'})

...这不仅仅是一个对象,实际上是可以执行的代码。因此,如果您在页面的其他地方定义了一个名为myFunction的函数并执行这个脚本,它将使用来自URL的数据被调用。

最酷的是:您可以创建一个脚本标记,并使用您的URL(带有回调参数)作为src属性,浏览器将运行它。这意味着您可以绕过“同源”安全策略(因为浏览器允许您从页面域以外的源运行脚本标记)。

这就是当你发出ajax请求时jQuery所做的(使用.ajax和'jsonp'作为dataType属性的值)。如。

$.ajax({
  url: 'http://example.com/datasource',
  dataType: 'jsonp',
  success: function(data) {
    // your code to handle data here
  }
});

这里,jQuery负责回调函数名和查询参数——使API与其他ajax调用相同。但与其他类型的ajax请求不同的是,如前所述,您不局限于从与页面相同的来源获取数据。


前言:

这个答案已经有六年多了。而JSONP的概念和应用并没有改变 (即答案的细节仍然有效),你应该这样做 尽可能使用CORS (即您的服务器或 API支持它,而 浏览器支持足够), 因为JSONP存在固有的安全风险。


JSONP(带填充的JSON)是一种常用的方法 绕过浏览器中的跨域策略。(您不允许对浏览器认为位于不同服务器上的网页发出AJAX请求。)

JSON和JSONP在客户端和服务器上的行为不同。JSONP请求不是使用XMLHTTPRequest和相关的浏览器方法分派的。相反,将创建一个<script>标记,其源被设置为目标URL。然后将这个脚本标记添加到DOM中(通常在<head>元素中)。

JSON请求:

var xhr = new XMLHttpRequest();

xhr.onreadystatechange = function () {
  if (xhr.readyState == 4 && xhr.status == 200) {
    // success
  };
};

xhr.open("GET", "somewhere.php", true);
xhr.send();

JSONP请求:

var tag = document.createElement("script");
tag.src = 'somewhere_else.php?callback=foo';

document.getElementsByTagName("head")[0].appendChild(tag);

JSON响应和JSONP响应之间的区别在于JSONP响应对象作为参数传递给回调函数。

JSON:

{ "bar": "baz" }

JSONP:

foo( { "bar": "baz" } );

这就是为什么您会看到包含回调参数的JSONP请求,以便服务器知道包装响应的函数名。

在浏览器计算<script>标记时(一旦请求完成),该函数必须存在于全局作用域中。


要注意的JSON响应和JSONP响应处理之间的另一个区别是,JSON响应中的任何解析错误都可以通过包装对responseText求值的尝试来捕获 try/catch语句中。由于JSONP响应的性质,响应中的解析错误将导致无法捕捉的JavaScript解析错误。

这两种格式都可以通过在初始化请求之前设置超时并在响应处理程序中清除超时来实现超时错误。


使用jQuery

使用jQuery来发出JSONP请求的好处在于,jQuery在后台为你完成了所有的工作。

jQuery默认要求你包含&callback=?在AJAX请求的URL中。jQuery将接受您指定的success函数,为其分配唯一的名称,并将其发布到全局作用域。然后它会替换问号?在回调= ?与它所分配的名称。


可比的JSON/JSONP实现

下面假设一个响应对象{"bar": "baz"}

JSON:

var xhr = new XMLHttpRequest();

xhr.onreadystatechange = function () {
  if (xhr.readyState == 4 && xhr.status == 200) {
    document.getElementById("output").innerHTML = eval('(' + this.responseText + ')').bar;
  };
};

xhr.open("GET", "somewhere.php", true);
xhr.send();

JSONP:

function foo(response) {
  document.getElementById("output").innerHTML = response.bar;
};

var tag = document.createElement("script");
tag.src = 'somewhere_else.php?callback=foo';

document.getElementsByTagName("head")[0].appendChild(tag);

JSONP是绕过浏览器同源策略的一种方法。怎么做?是这样的:

这里的目标是向otherdomain.com发出请求,并在响应中提醒该名称。通常我们会发出一个AJAX请求:

$.get('otherdomain.com', function (response) {
  var name = response.name;
  alert(name);
});

但是,由于请求将发送到不同的域,因此它将不起作用。

不过,我们可以使用<script>标记来发出请求。<script src="otherdomain.com"></script>和$.get('otherdomain.com')都会产生相同的请求:

GET otherdomain.com

问:但是如果我们使用<script>标记,我们如何访问响应?如果我们想提醒它,我们需要进入它。

A:呃,我们不能。但是我们可以这样做——定义一个使用响应的函数,然后告诉服务器用JavaScript响应,用响应作为参数调用我们的函数。

问:但是如果服务器不为我们做这些,只愿意返回JSON给我们怎么办?

A:那我们就不能用了。JSONP要求服务器配合。

Q:必须使用<script>标记是丑陋的。

A:像jQuery这样的库使它更好。例:

$.ajax({
    url: "http://otherdomain.com",
    jsonp: "callback",
    dataType: "jsonp",
    success: function( response ) {
        console.log( response );
    }
});

它通过动态创建<script>标记DOM元素来工作。

Q: <script>标签只做GET请求-如果我们想做一个POST请求怎么办?

A:那么JSONP对我们不适用了。

问:没关系,我只是想做一个GET请求。JSONP太棒了,我要去用它——谢谢!

A:事实上,它并没有那么棒。这真的只是个黑客。而且这也不是最安全的方法。既然CORS是可用的,您应该尽可能使用它。


我发现了一篇有用的文章,它也很清楚地用简单的语言解释了这个主题。Link是JSONP

一些值得注意的点是:

JSONP早于CORS。 这是一种从不同领域检索数据的伪标准方法, 它具有有限的CORS特性(只有GET方法)

工作情况如下:

< script src = "网址吗?Callback =function_name">包含在HTML代码中 当执行第1步时,它会将具有相同函数名(url参数中给出的函数名)的函数作为响应。 如果代码中存在具有给定名称的函数,则它将与数据一起执行,如果有数据,则作为该函数的参数返回。