什么是 JSONP,为什么要创建它?
Posted
技术标签:
【中文标题】什么是 JSONP,为什么要创建它?【英文标题】:What is JSONP, and why was it created? 【发布时间】:2011-01-05 06:21:55 【问题描述】:我了解 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 有了清晰而全面的描述。
【问题讨论】:
作为记录,如果你不信任你正在与之交谈的服务器 100%,请不要使用 JSONP。如果它被入侵,您的网页将被轻微入侵。 还要注意 JSONP can be hijacked 如果没有正确实现。 我要感谢 JSONP 的作者,他给出了它背后的理念:Bob Ippolito's archive on JSONP。他将 JSONP 介绍为“一种用于跨域数据获取的脚本标记方法的新技术不可知标准方法”。 对于那些可能通过搜索其他内容十多年后到达这里并感到困惑的人:JSON-P 现在也是用于处理 JSON 的 Java API 的名称,主要是解析和写它。它反映了 XML StAX 和 DOM API,分别用于流式输入/输出和文档建模。它支持用于查询的 JSON 指针,例如用于 XML 的 XPath。我认为它还打算提供通过 JSON Patch 转换 JSON 的方法,例如带有 XSLT 的 XML 和 Java XML Transformer API,但它仍然不如 XSLT 先进。这个问题是关于脚本注入的。 【参考方案1】:因为您可以要求服务器为返回的 JSON 对象添加前缀。例如
function_prefix(json_object);
为了让浏览器将eval
“内联”JSON 字符串作为表达式。这个技巧使服务器可以直接在客户端浏览器中“注入”javascript代码,并绕过“同源”限制。
也就是说,可以实现跨域数据交换。
通常,XMLHttpRequest
不允许直接跨域数据交换(需要通过同一域中的服务器),而:
<script src="some_other_domain/some_data.js&prefix=function_prefix
>` 可以从不同于源域的域访问数据。
另外值得注意的是:即使在尝试这种“技巧”之前服务器应该被认为是“可信的”,但对象格式等可能发生变化的副作用是可以控制的。如果使用function_prefix
(即适当的js函数)接收JSON对象,则该函数可以在接受/进一步处理返回的数据之前进行检查。
【讨论】:
“附加前缀”令人困惑:) 不确定包含辐射的警告是否准确。恶意服务器可能会返回function_prefix();super_dangerous_functionwindow.open('youvebeenhacked!')()
【参考方案2】:
其实并不复杂……
假设您在域 example.com
上,并且您想向域 example.net
发出请求。为此,您需要跨域边界,这是大多数浏览器领域中的no-no。
绕过此限制的一项是<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 年),CORS 是推荐的方法,而不是 JSONRequest。 JSONP 对于旧版浏览器支持仍然有用,但考虑到安全隐患,除非您别无选择,否则 CORS 是更好的选择。
【讨论】:
请注意,使用 JSONP 有一些安全隐患。由于 JSONP 是真正的 javascript,它可以做 javascript 可以做的所有事情,因此您需要信任 JSONP 数据的提供者。我在这里写了一篇关于它的博客文章:erlend.oftedal.no/blog/?blogid=97 在 JSONP 中是否真的存在任何 不,它没有。如果您信任它来提供 javascript,同样适用于 JSONP。 值得注意的是,您可以通过更改数据返回方式来稍微提高安全性。如果您以真正的 JSON 格式返回脚本,例如 mycallback('"foo":"bar"')(注意参数现在是字符串),那么您可以自己手动解析数据以“清理”它之前评估。 CURL 是服务器端解决方案,而不是客户端。它们有两种不同的用途。【参考方案3】:JSONP 确实是克服XMLHttpRequest 同域策略的简单技巧。 (如您所知,无法将 AJAX (XMLHttpRequest) 请求发送到不同的域。)
所以 - 我们必须使用 script html 标签,而不是使用 XMLHttpRequest,您通常使用这些标签来加载 js 文件,以便 js 从另一个获取数据领域。听起来很奇怪?
事实是 - 结果表明 script 标签可以以类似于 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?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。 (命名非常糟糕的技术,因为它实际上与大多数人认为的“填充”无关。)
【讨论】:
感谢脚本标签的解释。我无法弄清楚 JSONP 是如何绕过跨域安全策略的。解释完之后,我觉得有点愚蠢,没有抓住重点...... 这是对 jvenema 答案的一个很好的补充答案 - 我不明白为什么回调是必要的,直到您指出 json 数据否则必须通过脚本元素访问。跨度> 感谢您提供如此清晰的解释。我希望我的大学教科书是像你这样的人写的:) 很好的解释,而不是前一个。当然-您的摘录“您通常用来加载 js 文件的那些,以便 js 从另一个域获取数据。听起来很奇怪?”也让我大开眼界。示例代码非常出色。 我更喜欢这个具体例子的解释,而不是公认的答案!谢谢!【参考方案4】:JSONP 通过构造“脚本”元素(在 HTML 标记中或通过 JavaScript 插入 DOM)来工作,该元素请求远程数据服务位置。响应是加载到浏览器的 javascript,带有预定义函数的名称以及传递的参数,即请求的 JSON 数据。当脚本执行时,该函数与 JSON 数据一起被调用,允许请求页面接收和处理数据。
进一步阅读访问:https://blogs.sap.com/2013/07/15/secret-behind-jsonp/
代码的客户端sn-p
<!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 该链接的内容现在可以在http://scn.sap.com/community/developer-center/front-end/blog/2013/07/15/secret-behind-jsonp获得。【参考方案5】: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 服务可在以下位置获得:this post
【讨论】:
感谢您发布您的答案!请注意,您应该在此处、此站点上发布答案的基本部分,否则您的帖子有被删除的风险See the FAQ where it mentions answers that are 'barely more than a link'. 如果您愿意,您仍然可以包含该链接,但仅作为“参考”。答案应该是独立的,不需要链接。【参考方案6】:一个简单的 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;
?>
【讨论】:
【参考方案7】:在了解 JSONP 之前,您需要了解 JSON 格式和 XML。目前Web上最常用的数据格式是XML,但是XML非常复杂。它使用户不方便处理嵌入在网页中的内容。
为了让JavaScript能够方便地交换数据,甚至作为数据处理程序,我们根据JavaScript对象的措辞,开发了一种简单的数据交换格式,即JSON。 JSON 可以用作数据,也可以用作 JavaScript 程序。
JSON 可以直接嵌入到 JavaScript 中,使用它们可以直接执行某些 JSON 程序,但是由于安全限制,浏览器沙盒机制禁用了跨域 JSON 代码执行。
为了让JSON可以在执行后传递,我们开发了一个JSONP。 JSONP 通过 JavaScript 回调功能和
简而言之,它解释了 JSONP 是什么,它解决了什么问题(何时使用它)。
【讨论】:
我对此投了反对票,因为我不相信 XML 是 2015 年 12 月网络上最常用的 dat 格式的说法。 它仍然不知道为什么使用 jsonp 而不是 json。所有这些安全限制来自哪里?为什么跨域可以使用jsonp而不能使用json?【参考方案8】:JSONP 代表带有 Padding 的 JSON。
这里是网站,有很好的例子,解释了从最简单的使用这种技术到最先进的平面JavaScript :
w3schools.com / JSONP
上述我最喜欢的技术之一是动态 JSON 结果,它允许将 JSON 发送到 URL 参数中的 PHP 文件,并让 PHP文件还会根据它获得的信息返回一个 JSON 对象。
jQuery also have facilities to use JSONP 之类的工具:
jQuery.ajax(
url: "https://data.acgov.org/resource/k9se-aps6.json?city=Berkeley",
jsonp: "callbackName",
dataType: "jsonp"
).done(
response => console.log(response)
);
【讨论】:
【参考方案9】:TL;DR
JSONP 是一个老把戏,发明用于绕过禁止我们获取位于不同网站(不同来源1) 比我们当前正在浏览的那个。
这个技巧通过使用<script>
标记从该位置请求 JSON,例如: "user":"Smith"
,但包装在一个函数中,实际 JSONP(“带有填充的 JSON” ):
peopleDataJSONP("user":"Smith")
以这种形式接收它使我们能够在 peopleDataJSONP
函数中使用数据。 JSONP 是一种不好的做法,不再需要,不要使用它(阅读下文)。
问题
假设我们想在ourweb.com
上使用托管在anotherweb.com
上的一些 JSON 数据(或任何原始数据)。如果我们要使用 GET 请求(想想XMLHttpRequest
,或fetch
调用,$.ajax
等),我们的浏览器会告诉我们不允许出现这个丑陋的错误:
如何获取我们想要的数据?好吧,<script>
标签不受整个服务器(origin1)的限制!这就是为什么我们可以从任何服务器(例如 CDN)加载 jQuery 或 Google Maps 等库而不会出现任何错误。
重点是:如果你仔细想想,这些库是实际的、可运行的 JS 代码(通常是一个包含所有逻辑的庞大函数)。但是原始数据呢? JSON 数据不是代码。没有什么可跑的;它只是纯文本。
因此,我们无法处理或操纵我们宝贵的数据。浏览器会下载我们<script>
标签指向的数据,在处理时它会抱怨:
wtf 这是我们加载的
"user":"Smith"
废话吗?这不是代码。我无法计算,语法错误!
JSONP 破解
利用这些数据的旧/老套路?如果我们可以使纯文本以某种方式可运行,我们就可以在运行时获取它。所以我们需要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
将是来自 anotherweb.com
的 JSON 数据。我们现在可以随心所欲地处理我们的数据。
不要使用 JSONP,使用 CORS
JSONP 是一种跨站点破解,有一些缺点:
我们只能执行 GET 请求 由于它是由简单脚本标签触发的 GET 请求,因此我们不会收到有用的错误或进度信息 还有一些安全问题,例如在您的客户端 JS 代码中运行可能会更改为恶意负载 它只解决了 JSON 数据的问题,但同源安全策略适用于其他数据(WebFonts、用 drawImage() 绘制的图像/视频...) 它既不优雅也不可读。要点是现在不需要使用它。
你应该read about CORS here,但它的要点是:
跨域资源共享 (CORS) 是一种使用 额外的 HTTP 头告诉浏览器给一个 web 应用程序 在一个来源运行,从不同的来源访问选定的资源 起源。 Web 应用程序在执行跨域 HTTP 请求时 请求具有不同来源(域、协议或 端口)从它自己的。
-
origin 由 3 个东西定义:protocol、port 和 host。因此,例如,
https://web.com
与http://web.com
(不同协议)和https://web.com:8081
(不同端口)和显然https://thatotherweb.net
(不同主机)的来源不同
【讨论】:
嘿伙计,这提供了 100% 的清晰度作为已批准答案的脚注!谢谢你……【参考方案10】:我试着用简单的方式解释:
JSONP 代表 JSON with Padding。 JSONP 是一种发送 JSON 数据的方法,无需担心跨域问题。为什么使用:
由于跨域策略,从另一个域请求文件可能会导致问题。
从另一个域请求外部脚本没有这个问题。
JSONP 利用了这个优势,使用脚本标签而不是 XMLHttpRequest 对象来请求文件。
服务器文件代码:
<?php
$myJSON = ' "name":"John", "age":30, "city":"New York" ';
echo "myFunc(".$myJSON.");";
?>
处理数据的函数:
function myFunc(myObj)
document.getElementById("demo").innerHTML = myObj.name;
【讨论】:
以上是关于什么是 JSONP,为什么要创建它?的主要内容,如果未能解决你的问题,请参考以下文章