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

IT技术 javascript json jsonp terminology
2020-12-11 00:26:33

我了解 JSON,但不了解 JSONP。维基百科关于 JSON 的文档是(曾经)JSONP 的顶级搜索结果。它是这样说的:

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

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

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

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

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


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


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

6个回答

其实也不是很复杂...

假设您在 domain 上example.com,并且您想向 domain 发出请求example.net要做到这一点,你需要跨域边界,一个没有没有在大多数browserland的。

绕过此限制的一项是<script>标签。当您使用脚本标签时,域限制会被忽略,但在正常情况下,您无法真正对结果任何事情,脚本只是被评估。

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

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

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

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

{ foo: 'bar' }

但是,对于 JSONP,当服务器收到“回调”参数时,它会以稍微不同的方式包装结果,返回如下内容:

mycallback({ foo: 'bar' });

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

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

现在,当脚本被加载时,它会被评估,你的函数将被执行。瞧,跨域请求!

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

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

CURL 是服务器端解决方案,而不是客户端。它们有两个不同的目的。
2021-02-07 00:26:33
请注意,使用 JSONP 有一些安全隐患。由于 JSONP 是真正的 javascript,它可以执行 javascript 可以执行的所有其他操作,因此您需要信任 JSONP 数据的提供者。我在这里写了一些关于它的博客文章:erlend.oftedal.no/blog/?blogid=97
2021-02-13 00:26:33
JSONP 中是否真的存在 <script> 标签中不存在的任何新安全隐患?使用脚本标签,浏览器隐含地信任服务器提供无害的 Javascript,浏览器会盲目地评估它。JSONP 会改变这个事实吗?似乎没有。
2021-02-16 00:26:33
不,它没有。如果您相信它会提供 javascript,同样的事情也适用于 JSONP。
2021-02-25 00:26:33
值得注意的是,您可以通过更改数据的返回方式来稍微提高安全性。如果你以真正的 JSON 格式返回脚本,例如 mycallback('{"foo":"bar"}')(注意参数现在是一个字符串),那么你可以自己手动解析数据,以便在“清理”它之前评估。
2021-03-05 00:26:33

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

感谢如此清晰的解释。我希望我的大学教科书是由像你这样的人写的 :)
2021-02-14 00:26:33
我喜欢这种带有具体例子的解释,而不是公认的答案!谢谢!
2021-02-23 00:26:33
很好的解释,而不是前一个。当然 - 你的摘录“你通常用来加载 js 文件的那些,以便 js 从另一个域获取数据。听起来很奇怪?” 也让我大开眼界。示例代码中非常有名。
2021-02-27 00:26:33
这是 jvenema 答案的一个很好的补充答案 - 我不明白为什么需要回调,直到您指出 json 数据否则必须通过脚本元素访问。
2021-03-03 00:26:33
感谢您的脚本标签解释。我无法弄清楚 JSONP 如何绕过跨域安全策略。解释完之后,我觉得有点愚蠢,错过了重点......
2021-03-04 00:26:33

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 . ")";
?>
顶部的链接现在只有 404s
2021-02-20 00:26:33

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

function_prefix(json_object);

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

也就是说,可以实现跨域的数据交换


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

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


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

“附加前缀”令人困惑:)
2021-02-10 00:26:33
不确定关于包含辐射的警告是否准确。恶意服务器可能会返回function_prefix();super_dangerous_function{window.open('youvebeenhacked!')}()
2021-03-03 00:26:33

TL; 博士

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

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

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

以这种形式接收它使我们能够在我们的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"})

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

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

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

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

浏览器将下载 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% 的清晰度!谢谢这个....
2021-03-09 00:26:33