有人能以通俗易懂的方式解释一下JSONP是什么吗?

425

我知道JSONP是带有填充的JSON

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

有人能向我解释这是如何工作的吗?


12
可能有用的链接:https://dev59.com/GHI95IYBdhLWcg3w-DL0 - Matt
4个回答

780

前言:

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


JSONP(带填充的JSON)是一种常用的方法,用于绕过Web浏览器中的跨域策略。(您不被允许通过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" } );

这就是为什么你会看到包含"callback"参数的JSONP请求,以便服务器知道将响应包装在哪个函数中。
这个函数必须存在于全局作用域中,在浏览器评估

15
这个解释简直值得收藏!向 @Matt 致以最高的赞扬,他最好地解释了 jsonp。我认真读了一整天的内容,这绝对是最好的解释。 - Eduardo La Hoz Miranda
非常好的业余人士解释。关于“这就是为什么您会看到包含“回调”参数的JSONP请求;因此,服务器知道将响应包装在哪个函数名称周围。” - 我只想补充一点,服务器甚至不必返回传递给回调函数的JSON对象 - 它可以返回任意的JavaScript代码(例如:http://www.jsontest.com/#code)。可能性是巨大的。 - thdoan
12
本质上,不仅可以向外国网络服务器请求数据,现在外国网络服务器还可以自动注入任何脚本到网页中,客户端甚至无法在执行之前查看代码!这是安全性方面从将JSON.parse替换为eval的举措向后倒退了很长一段路程。 - Levi Haskell
3
我不确定 JSONP 的目的是什么。如果服务器可以添加 padding,那么它肯定也可以在响应中加上 Access-Control-Allow-Origin 标头,对吗? - Andrew Savinykh
6
你是正确的。然而,除了服务器添加“Access-Control-Allow-Origin”头之外,你还需要拥有支持CORS(https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS#Browser_compatibility)的浏览器。IE7 没有支持,IE8和9则在 XMLHttpRequest 之外提供支持(请注意,这个回答是来自2010年!)。考虑到微软已经不再支持这些浏览器,并考虑到JSONP带来的安全隐患,应该尽可能使用CORS(如果可用)。 - Matt
显示剩余3条评论

74

假设你有一个URL,它提供了JSON格式的数据,例如:

{'field': 'value'}

假设你有一个类似的URL,但使用了JSONP,并且传递了回调函数名字'myCallback'(通常通过添加查询参数'callback'实现,例如http://example.com/dataSource?callback=myCallback)。那么它将返回:

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

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

很酷的是:你可以创建一个脚本标签,并将包含callback参数的URL作为src属性使用,浏览器会运行它。这意味着你可以规避“同源策略”(因为浏览器允许你从页面域之外的源运行脚本标签)。

这就是当你发起一个ajax请求时jQuery所做的事情(使用dataType属性设置值为'jsonp'的.ajax)。例如:

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

在这里,jQuery会处理回调函数名称和查询参数 - 使API与其他ajax调用相同。但与其他类型的ajax请求不同,正如前面提到的,你不受限于从与你的页面相同的来源获取数据。


7
好的,最后终于清楚了。 - Paranoid Android
终于有人知道如何传递信息了。谢谢@sje397。 - Phil

18

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

enter image description here

这里的目标是向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>标签,我们怎么才能访问响应内容呢?如果我们想要弹出它,我们需要访问它。

答:嗯,我们做不到。但是这里有一个解决方法——定义一个使用响应的函数,然后告诉服务器用调用我们的函数并把响应作为其参数的JavaScript来响应。

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

答:那么我们将无法使用它。JSONP需要服务器的合作。

问:必须使用<script>标签真的很丑陋。

答:像jQuery这样的库使它更美观。例如:

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

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

问: <script> 标签只能发出 GET 请求 - 如果我们想要发出 POST 请求怎么办?

答:那么 JSONP 对我们就没用了。

问:那很好,我只想发出 GET 请求。JSONP 真的很棒,我打算去使用它 - 谢谢!

答:实际上,它并不是那么棒。它真的只是一个 hack。并且它并不安全。现在 CORS 可用后,应该尽可能使用它。


那个交互图真的帮了很多,谢谢! - Tarocco

3
我找到了一篇有用的文章,它用简单易懂的语言解释了这个主题。链接在JSONP。以下是需要注意的几点:
  1. JSONP是CORS之前就存在的。
  2. 它是从不同域获取数据的伪标准方式。
  3. 它只支持有限的CORS功能(仅支持GET方法)。
工作原理如下:
  1. 在HTML代码中包含<script src="url?callback=function_name">
  2. 当执行步骤1时,它将发送一个具有与URL参数中给定函数名称相同的函数作为响应。
  3. 如果代码中存在具有给定名称的函数,则该函数将被执行,并将数据(如果有)作为该函数的参数返回。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接