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

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

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

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

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

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


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


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


当前回答

JSONP实际上是克服XMLHttpRequest同域策略的一个简单技巧。(如您所知,不能将AJAX(XMLHttpRequest)请求发送到其他域。)

因此,我们必须使用脚本HTML标记,而不是使用XMLHttpRequest,这些标记通常用于加载js文件,以便js从另一个域获取数据。听起来很奇怪?

事实是,脚本标记可以以类似于XMLHttpRequest的方式使用!看看这个:

script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://www.someWebApiServer.com/some-data';

加载数据后,您将得到一个如下所示的脚本段:

<script>
{['some string 1', 'some data', 'whatever data']}
</script>

然而,这有点不方便,因为我们必须从脚本标记中获取这个数组。因此,JSONP的创建者决定,这将更好地工作(而且是这样):

script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://www.someWebApiServer.com/some-data?callback=my_callback';

注意到那边的my_callback函数了吗?因此,当JSONP服务器收到您的请求并找到回调参数时,它将返回以下内容,而不是返回纯js数组:

my_callback({['some string 1', 'some data', 'whatever data']});

看看利润在哪里:现在我们得到了自动回调(my_callback),一旦我们得到数据,它就会被触发。这就是关于JSONP的所有知识:它是回调和脚本标记。

注意:这些是JSONP用法的简单示例,它们不是可用于生产的脚本。

基本JavaScript示例(使用JSONP的简单Twitter提要)

<html>
    <head>
    </head>
    <body>
        <div id = 'twitterFeed'></div>
        <script>
        function myCallback(dataWeGotViaJsonp){
            var text = '';
            var len = dataWeGotViaJsonp.length;
            for(var i=0;i<len;i++){
                twitterEntry = dataWeGotViaJsonp[i];
                text += '<p><img src = "' + twitterEntry.user.profile_image_url_https +'"/>' + twitterEntry['text'] + '</p>'
            }
            document.getElementById('twitterFeed').innerHTML = text;
        }
        </script>
        <script type="text/javascript" src="http://twitter.com/status/user_timeline/padraicb.json?count=10&callback=myCallback"></script>
    </body>
</html>

基本jQuery示例(使用JSONP的简单Twitter提要)

<html>
    <head>
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
        <script>
            $(document).ready(function(){
                $.ajax({
                    url: 'http://twitter.com/status/user_timeline/padraicb.json?count=10',
                    dataType: 'jsonp',
                    success: function(dataWeGotViaJsonp){
                        var text = '';
                        var len = dataWeGotViaJsonp.length;
                        for(var i=0;i<len;i++){
                            twitterEntry = dataWeGotViaJsonp[i];
                            text += '<p><img src = "' + twitterEntry.user.profile_image_url_https +'"/>' + twitterEntry['text'] + '</p>'
                        }
                        $('#twitterFeed').html(text);
                    }
                });
            })
        </script>
    </head>
    <body>
        <div id = 'twitterFeed'></div>
    </body>
</html>

JSONP代表带填充的JSON。(这项技术的名称很差,因为它和大多数人认为的“填充”毫无关系。)

其他回答

因为您可以要求服务器为返回的JSON对象添加前缀。例如

function_prefix(json_object);

以便浏览器将JSON字符串作为表达式“内联”求值。这一技巧使服务器可以直接在客户端浏览器中“注入”javascript代码,并且绕过“同源”限制。

换句话说,您可以实现跨域数据交换。


通常,XMLHttpRequest不允许直接进行跨域数据交换(需要通过同一域中的服务器),而:

<script src=“some_other_domain/some_data.js&prefix=function_prefix>`可以从不同于源域的域访问数据。


同样值得注意的是:即使在尝试这种“技巧”之前,服务器应该被认为是“可信的”,但对象格式等可能改变的副作用也可以得到控制。如果使用function_prefix(即正确的js函数)接收JSON对象,则所述函数可以在接受/进一步处理返回的数据之前执行检查。

出身背景

您应该尽可能使用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" }

日本电话:

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

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

当浏览器评估<script>标记时(请求完成后),该函数必须存在于全局范围中。

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

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

使用jQuery

使用jQuery发出JSONP请求的有用之处在于,jQuery在后台为您完成所有工作。

默认情况下,jQuery要求您包含&callback=?在AJAX请求的URL中。jQuery将接受您指定的成功函数,为其分配一个唯一的名称,并将其发布到全局范围中。然后它将取代问号?在回调中=?使用它指定的名称。

比较类似的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();

日本电话:

 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通过构造一个“脚本”元素(以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之前,您需要了解JSON格式和XML。目前,web上最常用的数据格式是XML,但XML非常复杂。这使得用户不方便处理嵌入在网页中的内容。

为了使JavaScript能够轻松地交换数据,即使作为数据处理程序,我们也根据JavaScript对象使用了措辞,并开发了一种简单的数据交换格式,即JSON。JSON可以用作数据,也可以用作JavaScript程序。

JSON可以直接嵌入JavaScript中,使用它们可以直接执行某些JSON程序,但由于安全限制,浏览器沙盒机制禁用跨域JSON代码执行。

为了使JSON可以在执行后传递,我们开发了一个JSONP。JSONP通过JavaScript回调功能和<script>标记绕过浏览器的安全限制。

简而言之,它解释了JSONP是什么,它解决了什么问题(何时使用它)。

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;

    ?>