什么是 JSONP,为什么创建它?

我了解 JSON,但不了解 JSONP。 Wikipedia 上有关 JSON 的文档是 JSONP 的最高搜索结果。它说:

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

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

第二个搜索结果来自一个叫Remy 的人 ,他写了关于 JSONP 的代码:

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

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


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


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

答案

实际上并不太复杂...

假设您使用的是example.com域,并且想向example.net域发出请求。要做到这一点,你需要跨域边界, 无无多数 browserland 的。

绕过此限制的一项是<script>标记。使用脚本标记时,将忽略域限制,但是在正常情况下,您实际上无法对结果任何事情,只是对脚本进行了评估。

输入JSONP 。当您向启用 JSONP 的服务器发出请求时,您将传递一个特殊参数,该参数告诉服务器有关您页面的一些信息。这样,服务器就可以用页面可以处理的方式很好地包装其响应。

例如,假设服务器需要一个名为callback的参数来启用其 JSONP 功能。然后您的请求将如下所示:

http://www.example.net/sample.aspx?callback=mycallback

没有 JSONP,这可能会返回一些基本的 JavaScript 对象,如下所示:

{ foo: 'bar' }

但是,使用 JSONP 时,服务器收到 “callback” 参数时,其包装结果会有所不同,返回如下所示:

mycallback({ foo: 'bar' });

如您所见,它现在将调用您指定的方法。因此,在页面中,您定义了回调函数:

mycallback = function(data){
  alert(data.foo);
};

现在,加载脚本后,将对其进行评估,然后将执行您的函数。瞧,跨网域要求!

值得注意的是 JSONP 的一个主要问题:您失去了对请求的大量控制。例如,没有 “不错” 的方法来找回正确的故障代码。结果,您最终会使用计时器来监视请求等,这总是让人怀疑。 JSONRequest的主张是一个很好的解决方案,它允许跨域脚本编写,维护安全性并允许对请求的适当控制。

如今(2015 年),与 JSONRequest 相比, CORS是推荐的方法。 JSONP 对于较旧的浏览器支持仍然有用,但是考虑到安全隐患,除非您别无选择,否则 CORS 是更好的选择。

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

因此 - 为了代替 js 使用XMLHttpRequest,我们必须使用脚本 HTML 标记(通常用于加载 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>

但是,这有点不方便,因为我们必须从script标签获取此数组。因此JSONP创建者认为这会更好(确实):

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

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

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

看看利润在哪里:现在我们获得自动回调(my_callback),一旦获取数据,该回调将被触发。
这就是关于JSONP的全部知识:这是一个回调和脚本标签。

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

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

<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 feed)

<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表示带有 Padding 的 JSON 。 (非常糟糕的技术,因为它实际上与大多数人认为的 “填充” 无关。)

JSONP 通过构造一个 “脚本” 元素(以 HTML 标记或通过 JavaScript 插入 DOM)来工作,该元素请求到远程数据服务位置。响应是使用预定义函数名称以及所传递的参数(即请求的 JSON 数据)加载到浏览器上的 JavaScript。执行脚本时,该函数将与 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 . ")";
?>

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

function_prefix(json_object);

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

换句话说,您可以进行跨域数据交换


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

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


同样值得注意的是:即使在尝试这种 “技巧” 之前,应将服务器视为 “受信任的” 服务器,也可以包含对象格式等可能发生变化的副作用。如果使用function_prefix (即适当的 js 函数)来接收 JSON 对象,则该函数可以在接受 / 进一步处理返回的数据之前执行检查。

JSONP 可以很好地解决跨域脚本错误。您可以只使用 JS 来使用 JSONP 服务,而不必在服务器端实现 AJAX 代理。

您可以使用b1t.co服务查看其工作方式。这是一项免费的 JSONP 服务,可让您缩小 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 中时,它将自动运行您应实现为回调函数的任何 JavascriptName:

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 服务进行练习:

一个使用 JSONP 的简单示例。

client.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>

server.php

<?php

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

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

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

    ?>

在理解 JSONP 之前,您需要了解 JSON 格式和 XML。当前,Web 上最常用的数据格式是 XML,但是 XML 非常复杂。它使用户不方便处理嵌入在 Web 页中的内容。

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

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

为了使 JSON 在执行后可以传递,我们开发了 JSONP。 JSONP 通过 JavaScript 回调功能和

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

已经给出了很好的答案,我只需要以 javascript 代码块的形式给出我的作品(我还将为跨域请求提供更现代,更好的解决方案:带有 HTTP 标头的 CORS):

JSONP:

1. client_jsonp.js

$.ajax({
    url: "http://api_test_server.proudlygeek.c9.io/?callback=?",
    dataType: "jsonp",
    success: function(data) {
        console.log(data);    
    }
});​​​​​​​​​​​​​​​​​​

2. server_jsonp.js

var http = require("http"),
    url  = require("url");

var server = http.createServer(function(req, res) {

    var callback = url.parse(req.url, true).query.callback || "myCallback";
    console.log(url.parse(req.url, true).query.callback);

    var data = {
        'name': "Gianpiero",
        'last': "Fiorelli",
        'age': 37
    };

    data = callback + '(' + JSON.stringify(data) + ');';

    res.writeHead(200, {'Content-Type': 'application/json'});
    res.end(data);
});

server.listen(process.env.PORT, process.env.IP);

console.log('Server running at '  + process.env.PORT + ':' + process.env.IP);

CORS

3.client_cors.js

$.ajax({
    url: "http://api_test_server.proudlygeek.c9.io/",
    success: function(data) {
        console.log(data);    
    }
});​

4. server_cors.js

var http = require("http"),
    url  = require("url");

var server = http.createServer(function(req, res) {
    console.log(req.headers);

    var data = {
        'name': "Gianpiero",
        'last': "Fiorelli",
        'age': 37
    };

    res.writeHead(200, {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*'
    });

    res.end(JSON.stringify(data));
});

server.listen(process.env.PORT, process.env.IP);

console.log('Server running at '  + process.env.PORT + ':' + process.env.IP);

JSONP表示带有Padding 的 JSON

这是该网站,上面有很多示例其中包括从最简单的使用此技术到最先进的平面 JavaScript 的解释:

w3schools.com / JSONP

上面描述的我最喜欢的技术之一是Dynamic JSON Result ,它允许将 JSON 发送到 URL 参数中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)
);