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

时间:2010-01-14 20:53:48

标签: javascript json jsonp terminology

我理解JSON,但不了解JSONP。 Wikipedia's document on JSON是(曾)JSONP的热门搜索结果。它说:

  

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

咦?什么电话?这对我没有任何意义。 JSON是一种数据格式。没有电话。

2nd search result来自名为Remy的人,他写了关于JSONP的文章:

  

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

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


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


附录:我刚刚在维基百科上创建了a new page for JSONP;它现在根据jvenema的答案对JSONP进行了清晰而全面的描述。

10 个答案:

答案 0 :(得分:1908)

实际上并不太复杂......

假设您在域 example.com ,并且您想向域 example.net 发出请求。为此,您需要在大多数浏览器中跨域边界,禁止

绕过此限制的一项是<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);
};

现在,当加载脚本时,它将被评估,并且您的函数将被执行。 Voila,跨域请求!

值得注意的是JSONP的一个主要问题:你失去了很多对请求的控制权。例如,没有“好的”方法来获取正确的故障代码。结果,您最终使用计时器来监视请求等,这总是有点可疑。 JSONRequest的提议是一个很好的解决方案,允许跨域脚本编写,维护安全性,并允许适当控制请求。

这些天(2015年),CORS是推荐的方法与JSONRequest。 JSONP对于较旧的浏览器支持仍然有用,但鉴于安全隐患,除非您没有选择,否则CORS是更好的选择。

答案 1 :(得分:674)

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

所以 - 我们不得不使用 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>

然而,这有点不方便,因为我们必须从脚本标记中获取此数组。所以 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 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提要)

<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 。 (技术名称很差,因为它与大多数人认为的“填充”无关。)

答案 2 :(得分:43)

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 . ")";
?>

答案 3 :(得分:39)

因为您可以要求服务器为返回的JSON对象附加前缀。 E.g

function_prefix(json_object);

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

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


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

<script src="some_other_domain/some_data.js&prefix=function_prefix&gt;`可以从不同于原点的域访问数据。


另外值得注意的是:即使在尝试那种“技巧”之前服务器应被视为“可信”,也可以包含对象格式等可能变化的副作用。如果使用function_prefix(即正确的js函数)来接收JSON对象,则所述函数可以在接受/进一步处理返回的数据之前执行检查。

答案 4 :(得分:18)

JSONP可以解决跨域脚本错误。您可以完全使用JS来使用JSONP服务,而无需在服务器端实现AJAX代理。

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

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服务:this post

答案 5 :(得分:12)

使用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;

    ?>    

答案 6 :(得分:11)

TL; DR

JSONP是一种旧技巧,其被发明来绕过安全限制,该安全限制禁止我们从其他服务器(不同的起源* )获取JSON数据。

该技巧通过使用一个<script>标记来实现,该标记从该位置请求JSON,例如:{ "user":"Smith" },但包装在一个函数中,即 actual JSONP (“带有填充的JSON”):

peopleDataJSONP({"user":"Smith"})

以这种形式接收它使我们能够使用peopleDataJSONP函数中的数据。 JSONP是一种不好的做法,请不要使用(请参阅下文)


问题

假设我们正在ourweb.com上导航,我们想从anotherweb.com获取JSON数据(或实际上是任何原始数据)。如果我们要使用GET请求(例如XMLHttpRequestfetch调用,$.ajax等),我们的浏览器会告诉我们,这个丑陋的错误是不允许的:

Chrome CORS console error

如何获取我们想要的数据?好吧,<script>标签不受整个服务器(来源*)的限制!这就是为什么我们可以从任何服务器(例如CDN)加载jQuery或Google Maps之类的库而不会出现任何错误。

重要点:如果考虑一下,这些库是可运行的实际JS代码(通常是包含所有逻辑的庞大函数)。但是原始数据? JSON数据不是代码。没什么可跑的。只是纯数据。

因此,无法处理或操纵我们的宝贵数据。浏览器将下载我们的<script>标记所指向的数据,并且在处理时会正确地抱怨:

wtf是我们加载的{"user":"Smith"}废话吗?这不是代码。我无法计算,语法错误!


JSONP黑客

利用旧数据的旧方法?我们需要该服务器以某种逻辑将其发送,因此,在加载该服务器时,您在浏览器中的代码将能够使用所述数据。因此,外部服务器向我们发送JS函数中的JSON数据。数据本身被设置为该函数的输入。看起来像这样:

peopleDataJSONP({"user":"Smith"})

这使其成为 JS代码,我们的浏览器将在不抱怨的情况下进行解析!与jQuery库完全一样。 现在,要做到这一点,客户端通常为此请求“请求” JSONP友好服务器:

<script src="https://anotherweb.com/api/data-from-people.json?myCallback=peopleDataJSONP"></script>

我们的浏览器将收到带有该函数名称的JSONP,因此我们需要在代码中使用一个具有相同名称的函数,如下所示:

const peopleDataJSONP = function(data){
  alert(data.user); // "Smith"
}

或者像这样,同样的结果:

function peopleDataJSONP(data){
  alert(data.user); // "Smith"
}

浏览器将下载并运行JSONP,调用我们的函数,其中参数data将是我们的JSON。现在,我们可以随意处理数据。


不要使用JSONP,请使用CORS

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

  • 我们只能执行GET请求
  • 由于这是一个由简单脚本标签触发的GET请求,因此我们不会收到有用的错误或进度信息
  • 还存在一些安全问题,例如在客户端JS代码中运行时,可以将其更改为恶意负载
  • 它仅解决JSON数据的问题,但是Same-Origin安全策略适用于其他数据(WebFonts,使用drawImage()绘制的图像/视频...)
  • 它不是很优雅也不可读。

要点是,现在不需要使用它

JSONP是从另一台服务器获取JSON数据的技巧,但是如果我们需要其他类型的跨站点内容,我们将违反相同的安全原则(Same-Origin)。

您应该read about CORS here,但要旨是:

跨源资源共享(CORS)是一种使用 其他HTTP标头,告诉浏览器提供Web应用程序 在一个来源运行,可以从其他来源访问选定的资源 起源。 Web应用程序在执行时会执行跨域HTTP请求 请求来源不同的资源(域,协议或 端口)。



*来源由3种内容定义:协议端口主机。因此,例如,https://web.comhttp://web.com(不同协议)和https://web.com:8081(不同端口)以及显然https://thatotherweb.net(不同主机)的起源不同

答案 7 :(得分:10)

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

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

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

为了使JSON可以在执行后传递,我们开发了一个JSONP。 JSONP通过JavaScript Callback功能和&lt;绕过浏览器的安全限制。脚本&gt;标签。

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

答案 8 :(得分:3)

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

JSONP:

1.client_jsonp.js

class ObjectA(object):
    def __init__(self, object_id):
        self.id = object_id
    def a_function(self):
        return self.id

from ObjectA import ObjectA
class ObjectB:
    def test():
        object_1 = ObjectA(1) # passing the id
        a_list = [ObjectA] # setting the list to be of type ObjectA (?)
        a_list.append(object_1)
        a_list[0].a_function() # Says I need to pass argument 'self'

2.server_jsonp.js

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

CORS

3.client_cors.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);

4.server_cors.js

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

答案 9 :(得分:0)

我试着用简单的方式解释:

  • JSONP 代表 JSON with Padding。
  • JSONP 是一种无需担心跨域问题即可发送 JSON 数据的方法。

为什么使用:

由于跨域策略,从另一个域请求文件可能会导致问题。

从另一个域请求外部脚本没有这个问题。

JSONP 利用了这个优势,使用 script 标签而不是 XMLHttpRequest 对象来请求文件。

服务器文件代码:

<?php
$myJSON = '{ "name":"John", "age":30, "city":"New York" }';

echo "myFunc(".$myJSON.");";
?>

处理数据的函数:

function myFunc(myObj) {
  document.getElementById("demo").innerHTML = myObj.name;
}