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

Che*_*eso 2040 javascript json jsonp terminology

我理解JSON,但不了解JSONP.维基百科关于JSON的文档是JSONP的最佳搜索结果.它说:

JSONP或"带填充的JSON"是JSON扩展,其中前缀被指定为调用本身的输入参数.

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

第二个搜索结果是由某些人叫雷米,谁写的这个约JSONP:

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

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


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


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

jve*_*ema 1977

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

假设您在域example.com上,并且您想向域example.net发出请求.要做到这一点,你需要跨越域边界,在大多数浏览器领域都是禁忌.

绕过此限制的一项是<script>标记.当您使用脚本标记时,将忽略域限制,但在正常情况下,您无法对结果执行任何操作,只会对脚本进行评估.

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

例如,假设服务器需要一个名为"callback"的参数来启用其JSONP功能.然后你的请求看起来像:

http://www.example.net/sample.aspx?callback=mycallback
Run Code Online (Sandbox Code Playgroud)

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

{ foo: 'bar' }
Run Code Online (Sandbox Code Playgroud)

但是,使用JSONP时,当服务器收到"callback"参数时,它会以稍微不同的方式包装结果,返回如下内容:

mycallback({ foo: 'bar' });
Run Code Online (Sandbox Code Playgroud)

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

mycallback = function(data){
  alert(data.foo);
};
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 请注意,使用JSONP会带来一些安全隐患.由于JSONP实际上是javascript,它可以执行javascript可以执行的所有其他操作,因此您需要信任JSONP数据的提供程序.我在这里写过som博客文章:http://erlend.oftedal.no/blog/?blogid = 97 (170认同)
  • JSONP中是否有任何新的安全隐含在<script>标记中不存在?使用脚本标记,浏览器隐式信任服务器以提供无害的Javascript,浏览器会盲目评估.JSONP会改变这个事实吗?它似乎没有. (70认同)
  • 不,它没有.你信任它提供javascript,同样适用于JSONP. (22认同)
  • 值得注意的是,您可以通过更改数据的返回方式来提高安全性.如果你以真正的JSON格式返回脚本,例如mycallback('{"foo":"bar"}')(注意参数现在是一个字符串),那么你可以自己手动解析数据以"清理"它之前评估. (15认同)
  • CURL是服务器端解决方案,而不是客户端.它们有两个不同的用途. (8认同)
  • @Stephen` {'foo':'bar'}`仍然无效JSON.双引号是必需的. (5认同)
  • {foo:'bar'}完全合法作为JS对象文字,这是我的例子所示 - JSONP可以正常使用对象文字或严格的JSON. (3认同)
  • @Cheeso:不,这是完全相同的问题.但有些人没有意识到这是一个问题.@jvenema:您仍然需要信任数据提供者:-) (2认同)
  • @Erlend,没有异议 - 任何时候你加载跨域的东西,你都必须至少对你正在加载的内容有一定的信任:) (2认同)
  • @Cheeso - 区别是 &lt;script&gt; 标签倾向于引用您自己控制下的资源。例如,如果您不信任 CDN,只需将脚本复制到您自己的服务器。如果您希望数据从您不直接控制的服务器直接传输到网页,则该选项对于 JSONP 是不可能的。jvenema 的解决方法是个好主意。 (2认同)
  • @CholthiPaulTtiopic - JSONP通过在页面中注入脚本来工作.返回的脚本可以随意乱用DOM.JSONP为您提供了来自数据源的XSS攻击,因此您必须完全信任数据源.(这就是为什么CORS更好,它不会遇到这个问题). (2认同)
  • 我发现 JSONP 的约定是为了方便而不是安全而设计的 (2认同)
  • 这个解释很好,很清楚!1. 服务器使用传递的回调包装 JSON 数据;2. 客户端使用`script`绕过了跨源限制;3.脚本URL加载后,执行全局回调函数 (2认同)

Tha*_*Guy 702

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';
Run Code Online (Sandbox Code Playgroud)

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

<script>
{['some string 1', 'some data', 'whatever data']}
</script>
Run Code Online (Sandbox Code Playgroud)

但是这有点不方便,因为我们必须从脚本标记中获取此数组.所以JSONP创建者决定这会更好(并且它是):

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

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

my_callback({['some string 1', 'some data', 'whatever data']});
Run Code Online (Sandbox Code Playgroud)

看看利润在哪里:现在我们得到自动回调(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>
Run Code Online (Sandbox Code Playgroud)

基本的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>
Run Code Online (Sandbox Code Playgroud)


JSONP代表带有填充的JSON.(技术名称很差,因为它与大多数人认为的"填充"无关.)

  • 感谢脚本标记说明.我无法弄清楚JSONP是如何绕过跨域安全策略的.在解释之后,我觉得有点愚蠢错过这一点...... (34认同)
  • 这是jvenema答案的一个非常好的补充答案 - 我不明白为什么回调是必要的,直到你指出json数据否则必须通过script元素访问. (13认同)
  • 谢谢你这么清楚的解释.我希望我的大学教科书是由像你这样的人写的:) (4认同)

Aja*_*vek 44

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>
Run Code Online (Sandbox Code Playgroud)

服务器端的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 . ")";
?>
Run Code Online (Sandbox Code Playgroud)

  • 现在顶部的链接只有404s (3认同)

jld*_*ont 39

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

function_prefix(json_object);

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

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


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

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


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

  • “附加前缀”令人困惑:) (2认同)

Car*_*lea 21

TL; 博士

JSONP 是一种古老的技巧,旨在绕过安全限制,该限制禁止我们获取与我们正在浏览的网站不同的网站(不同的来源1)中的 JSON 数据。

该技巧的工作原理是使用<script>从该位置请求 JSON的标签,例如: { "user":"Smith" },但包装在一个函数中,即实际的 JSONP(“带填充的 JSON”):

peopleDataJSONP({"user":"Smith"})
Run Code Online (Sandbox Code Playgroud)

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


问题

假设我们想要使用ourweb.com托管在anotherweb.com. 如果我们要使用 GET 请求(thinkXMLHttpRequestfetchcall$.ajax等),我们的浏览器会告诉我们这个丑陋的错误是不允许的:

Chrome CORS 控制台错误

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

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

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

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


JSONP 黑客

使用该数据的旧/hacky 方式?如果我们能以某种方式使纯文本可运行,我们就可以在运行时抓取它。所以我们需要anotherweb.com用一些逻辑来发送它,所以当它被加载时,你在浏览器中的代码将能够使用这些数据。我们需要做两件事:1)以一种可以运行的方式获取数据,以及2)在客户端中编写一些代码,以便在数据运行时调用此代码并使用数据。

对于1)我们要求外部服务器向我们发送 JS 函数内的 JSON 数据。数据本身被设置为该函数的输入。它看起来像这样:

peopleDataJSONP({"user":"Smith"})
Run Code Online (Sandbox Code Playgroud)

这使得我们的浏览器可以解析和运行JS 代码而不会抱怨!与 jQuery 库完全一样。为了接收这样的数据,客户端“向”JSONP 友好的服务器“询问”它,通常是这样完成的:

<script src="https://anotherweb.com/api/data-from-people.json?myCallback=peopleDataJSONP"></script>
Run Code Online (Sandbox Code Playgroud)

根据2),由于我们的浏览器将接收具有该函数名称的 JSONP,因此我们需要在代码中具有相同名称的函数,如下所示:

function peopleDataJSONP(data){
  alert(data.user); // "Smith"
}
Run Code Online (Sandbox Code Playgroud)

浏览器将下载 JSONP 并运行它,它调用我们的函数,其中参数data将是来自 的 JSON 数据anotherweb.com。我们现在可以对我们的数据做任何我们想做的事情。


不要使用 JSONP,使用 CORS

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

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

要点是现在没有必要使用它

您应该在此处阅读有关 CORS 的信息,但其要点是:

跨域资源共享 (CORS) 是一种机制,它使用额外的 HTTP 标头告诉浏览器让在一个源上运行的 Web 应用程序可以访问来自不同源的选定资源。当 Web 应用程序请求与自身具有不同来源(域、协议或端口)的资源时,它会执行跨源 HTTP 请求。



  1. origin由 3 个东西定义:protocolporthost。因此,例如,https://web.com是与http://web.com(不同的协议)和https://web.com:8081(不同的端口)和显然https://thatotherweb.net(不同的主机)不同的来源

  • 嘿伙计,这作为已批准答案的脚注提供了 100% 的清晰度!谢谢这个.... (4认同)

dar*_*awk 19

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"});
Run Code Online (Sandbox Code Playgroud)

因此当这个get作为src加载到你的js中时,它会自动运行你应该实现的任何JavascriptName作为你的回调函数:

function minifyResultsCallBack(data)
{
    document.getElementById("results").innerHTML = JSON.stringify(data);
}
Run Code Online (Sandbox Code Playgroud)

要实际进行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);
}
Run Code Online (Sandbox Code Playgroud)

可以在以下位置获得分步示例和jsonp Web服务以进行练习:此帖子

  • 谢谢你发布你的答案!请注意,您应该在此网站上发布答案的基本部分,或者删除您的帖子风险[请参阅常见问题解答,其中提到的答案"仅仅是一个链接".](http:// stackoverflow. com/faq#deletion)如果您愿意,您仍可以包含该链接,但仅作为"参考".答案应该独立而不需要链接. (2认同)

sar*_*eph 13

一个使用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>
Run Code Online (Sandbox Code Playgroud)

server.php

  <?php

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

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

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

    ?>    
Run Code Online (Sandbox Code Playgroud)


Mar*_*ton 10

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

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

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

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

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

  • 我赞成这一点,因为我不相信在2015年12月,XML是网络上最常用的数据格式. (4认同)
  • 它仍然不明白为什么使用 jsonp 而不是 json。所有这些安全限制从何而来?为什么跨域可以用jsonp而不能用json呢? (2认同)