我理解JSON,但不理解JSONP。维基百科关于JSON的文档是JSONP的最高搜索结果。上面写着:

JSONP或“带填充的JSON”是一个JSON扩展,其中前缀被指定为调用本身的输入参数。

嗯?什么电话?这对我来说毫无意义。JSON是一种数据格式。没有电话。

第二个搜索结果来自一个叫雷米的人,他写了关于JSONP的文章:

JSONP是脚本标记注入,将服务器的响应传递给用户指定的函数。

我可以理解,但这仍然没有任何意义。


那么什么是JSONP?为什么创建它(它解决了什么问题)?我为什么要用它?


附录:我刚刚在维基百科上为JSONP创建了一个新页面;根据jvenema的回答,它现在对JSONP有了清晰而透彻的描述。


当前回答

JSONP通过构造一个“脚本”元素(以HTML标记或通过JavaScript插入到DOM中)来工作,该元素请求远程数据服务位置。响应是加载到浏览器的javascript,带有预定义函数的名称以及传递的参数,该参数是请求的JSON数据。当脚本执行时,该函数与JSON数据一起被调用,从而允许请求页面接收和处理数据。

更多阅读请访问:https://blogs.sap.com/2013/07/15/secret-behind-jsonp/

客户端代码段

    <!DOCTYPE html>
    <html lang="en">
    <head>
     <title>AvLabz - CORS : The Secrets Behind JSONP </title>
     <meta charset="UTF-8" />
    </head>
    <body>
      <input type="text" id="username" placeholder="Enter Your Name"/>
      <button type="submit" onclick="sendRequest()"> Send Request to Server </button>
    <script>
    "use strict";
    //Construct the script tag at Runtime
    function requestServerCall(url) {
      var head = document.head;
      var script = document.createElement("script");

      script.setAttribute("src", url);
      head.appendChild(script);
      head.removeChild(script);
    }

    //Predefined callback function    
    function jsonpCallback(data) {
      alert(data.message); // Response data from the server
    }

    //Reference to the input field
    var username = document.getElementById("username");

    //Send Request to Server
    function sendRequest() {
      // Edit with your Web Service URL
      requestServerCall("http://localhost/PHP_Series/CORS/myService.php?callback=jsonpCallback&message="+username.value+"");
    }    

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

服务器端PHP代码

<?php
    header("Content-Type: application/javascript");
    $callback = $_GET["callback"];
    $message = $_GET["message"]." you got a response from server yipeee!!!";
    $jsonResponse = "{\"message\":\"" . $message . "\"}";
    echo $callback . "(" . $jsonResponse . ")";
?>

其他回答

JSONP用法的一个简单示例。

客户端.html

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


    <input type="button" id="001" onclick=gO("getCompany") value="Company"  />
    <input type="button" id="002" onclick=gO("getPosition") value="Position"/>
    <h3>
    <div id="101">

    </div>
    </h3>

    <script type="text/javascript">

    var elem=document.getElementById("101");

    function gO(callback){

    script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = 'http://localhost/test/server.php?callback='+callback;
    elem.appendChild(script);
    elem.removeChild(script);


    }

    function getCompany(data){

    var message="The company you work for is "+data.company +"<img src='"+data.image+"'/   >";
    elem.innerHTML=message;
}

    function getPosition(data){
    var message="The position you are offered is "+data.position;
    elem.innerHTML=message;
    }
    </script>
    </body>
    </html>

服务器.php

  <?php

    $callback=$_GET["callback"];
    echo $callback;

    if($callback=='getCompany')
    $response="({\"company\":\"Google\",\"image\":\"xyz.jpg\"})";

    else
    $response="({\"position\":\"Development Intern\"})";
    echo $response;

    ?>    

JSONP通过构造一个“脚本”元素(以HTML标记或通过JavaScript插入到DOM中)来工作,该元素请求远程数据服务位置。响应是加载到浏览器的javascript,带有预定义函数的名称以及传递的参数,该参数是请求的JSON数据。当脚本执行时,该函数与JSON数据一起被调用,从而允许请求页面接收和处理数据。

更多阅读请访问:https://blogs.sap.com/2013/07/15/secret-behind-jsonp/

客户端代码段

    <!DOCTYPE html>
    <html lang="en">
    <head>
     <title>AvLabz - CORS : The Secrets Behind JSONP </title>
     <meta charset="UTF-8" />
    </head>
    <body>
      <input type="text" id="username" placeholder="Enter Your Name"/>
      <button type="submit" onclick="sendRequest()"> Send Request to Server </button>
    <script>
    "use strict";
    //Construct the script tag at Runtime
    function requestServerCall(url) {
      var head = document.head;
      var script = document.createElement("script");

      script.setAttribute("src", url);
      head.appendChild(script);
      head.removeChild(script);
    }

    //Predefined callback function    
    function jsonpCallback(data) {
      alert(data.message); // Response data from the server
    }

    //Reference to the input field
    var username = document.getElementById("username");

    //Send Request to Server
    function sendRequest() {
      // Edit with your Web Service URL
      requestServerCall("http://localhost/PHP_Series/CORS/myService.php?callback=jsonpCallback&message="+username.value+"");
    }    

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

服务器端PHP代码

<?php
    header("Content-Type: application/javascript");
    $callback = $_GET["callback"];
    $message = $_GET["message"]." you got a response from server yipeee!!!";
    $jsonResponse = "{\"message\":\"" . $message . "\"}";
    echo $callback . "(" . $jsonResponse . ")";
?>

这是我为那些需要的人所做的ELI5(像我一样解释我)尝试。

TL;博士

JSONP是为了绕过浏览器中的安全限制而发明的一个老把戏,该限制禁止我们获取不同于我们自己的网站/服务器(称为不同的源1)中的数据。

该技巧通过使用<script>标签从其他地方加载JSON(例如:{“city”:“Barcelona”})来实现,这将向我们发送封装在函数中的数据,即实际的JSONP(“带填充的JSON”):

tourismJSONP({"city":"Barcelona"})

通过这种方式接收数据,我们可以在旅游JSONP功能中使用数据。JSONP是一种糟糕的做法,不再需要,不要使用它(请在最后阅读)。


问题是

假设我们想在我们的web.com上使用另一个web.com上托管的一些JSON数据(或任何原始数据)。如果我们要使用GET请求(想想XMLHttpRequest,或fetch调用,$.ajax等),我们的浏览器会告诉我们,这是不允许的,并且会出现以下错误:

这是一个内容安全策略限制错误,旨在保护用户免受某些攻击。您应该正确配置它(请参见末尾)。

JSONP技巧对我们有何帮助?好吧,<script>标签不受整个服务器(origin1)的限制!这就是为什么我们可以从任何服务器加载像jQuery或GoogleMaps这样的库。

重要的一点是:如果你仔细想想,这些库是实际的、可运行的JS代码(通常是一个包含所有逻辑的大型函数)。但原始数据不是代码。没有什么可跑的;这只是纯文本。

因此,浏览器将下载<script>标记所指向的数据,在处理时,它会理所当然地抱怨:

这是我们装的垃圾吗?这不是代码。我不会计算!


旧的JSONP黑客

如果我们能让纯文本以某种方式运行,我们就能在运行时获取它。我们需要另一个web.com将其作为代码发送,因此当下载时,浏览器将运行它。我们只需要两件事:1)以可以运行的方式获取数据,2)在客户端中编写一些代码,以便在数据运行时调用我们的函数并使用数据。

对于1)如果外部服务器是JSONP友好的,我们将要求提供如下数据:

<script src="https://anotherweb.com/api/tourism-data.json?myCallback=tourismJSONP"></script>

所以我们会收到这样的信息:

tourismJSONP({"city":"Barcelona"})

这使得我们可以使用JS代码进行交互。

根据2),我们需要在代码中编写一个同名函数,如下所示:

function tourismJSONP(data){
  alert(data.city); // "Barcelona"
}

浏览器将下载JSONP并运行它,它调用我们的函数,其中参数数据将是来自另一个web.com的JSON数据。我们现在可以随心所欲地处理数据。


不要使用JSONP,使用CORS

JSONP是一种跨站点黑客,有一些缺点:

我们只能执行GET请求由于这是一个由简单脚本标记触发的GET请求,因此我们不会得到有用的错误或进度信息还有一些安全问题,比如在客户端JS代码中运行,可能会被更改为恶意负载它只解决了JSON数据的问题,但同源安全策略适用于其他数据(WebFonts、使用drawImage()绘制的图像/视频…)它既不优雅,也不可读。

重要的是现在没有必要使用它。

您应该在这里阅读CORS,但其要点是:

跨源资源共享(CORS)是一种使用额外的HTTP头,告诉浏览器提供web应用程序在一个源上运行,从另一个源访问所选资源起源当web应用程序执行跨源HTTP请求时请求具有不同来源(域、协议或端口)。



起源由三个方面定义:协议、端口和主机。所以https://web.com与http://web.com(不同的协议)https://web.com:8081(不同的港口),显然https://thatotherweb.net(不同主机)

JSONP代表带填充的JSON。

这是一个网站,有很多例子,从最简单的使用这一技术到最先进的平面JavaScript的解释:

w3schools.com/JSONP

上面描述的我最喜欢的技术之一是Dynamic JSON Result,它允许以URL参数将JSON发送到PHP文件,并让PHP文件根据获得的信息返回JSON对象。

jQuery等工具也有使用JSONP的工具:

jQuery.ajax({
  url: "https://data.acgov.org/resource/k9se-aps6.json?city=Berkeley",
  jsonp: "callbackName",
  dataType: "jsonp"
}).done(
  response => console.log(response)
);

JSONP是解决跨域脚本错误的好方法。您可以使用纯JS的JSONP服务,而无需在服务器端实现AJAX代理。

您可以使用b1t.co服务查看其工作原理。这是一个免费的JSONP服务,让您可以缩小URL。以下是用于服务的url:

http://b1t.co/Site/api/External/MakeUrlWithGet?callback=[resultsCallBack]&url=[escapedUrlToMinify]

例如呼叫,http://b1t.co/Site/api/External/MakeUrlWithGet?callback=whateverJavascriptName&url=google.com

将返回

whateverJavascriptName({"success":true,"url":"http://google.com","shortUrl":"http://b1t.co/54"});

因此,当该get作为src加载到js中时,它将自动运行whateverJavascriptName,您应该将其实现为回调函数:

function minifyResultsCallBack(data)
{
    document.getElementById("results").innerHTML = JSON.stringify(data);
}

要实际执行JSONP调用,可以通过几种方式(包括使用jQuery)执行,但这里有一个纯JS示例:

function minify(urlToMinify)
{
   url = escape(urlToMinify);
   var s = document.createElement('script');
   s.id = 'dynScript';
   s.type='text/javascript';
   s.src = "http://b1t.co/Site/api/External/MakeUrlWithGet?callback=resultsCallBack&url=" + url;
   document.getElementsByTagName('head')[0].appendChild(s);
}

一个循序渐进的示例和一个要练习的jsonp web服务可以在以下位置获得: