提问人:Cheeso 提问时间:1/15/2010 最后编辑:mikemaccanaCheeso 更新时间:6/17/2023 访问量:598431
什么是 JSONP,为什么要创建它?
What is JSONP, and why was it created?
问:
我懂 JSON,但不懂 JSONP。维基百科关于JSON的文档是JSONP的顶级搜索结果。它是这样说的:
JSONP 或“带填充的 JSON”是一个 JSON 扩展,其中前缀被指定为调用本身的输入参数。
哼?什么叫?这对我来说没有任何意义。JSON是一种数据格式。没有电话。
第二个搜索结果来自一个名叫 Remy 的人,他写了这个关于 JSONP 的文章:
JSONP 是脚本标记注入,将响应从服务器传递到用户指定的函数。
我能理解这一点,但这仍然没有任何意义。
那么什么是JSONP?为什么创建它(它解决了什么问题)?我为什么要使用它?
附录:我刚刚在维基百科上为JSONP创建了一个新页面;根据 jvenema 的回答,它现在对 JSONP 进行了清晰而全面的描述。
答:
因为您可以要求服务器在返回的 JSON 对象前面添加前缀。例如
function_prefix(json_object);
以便浏览器将 JSON 字符串作为表达式“内联”。这个技巧使服务器可以直接在客户端浏览器中“注入”javascript代码,并绕过“同源”限制。eval
换句话说,您可以实现跨域数据交换。
通常,不允许直接进行跨域数据交换(需要通过同一域中的服务器),而:XMLHttpRequest
<script src="some_other_domain/some_data.js&prefix=function_prefix
>,可以从与源域不同的域访问数据。
另外值得注意的是:尽管在尝试这种“技巧”之前应该将服务器视为“受信任的”,但可以控制对象格式等可能更改的副作用。如果使用(即适当的js函数)来接收JSON对象,则该函数可以在接受/进一步处理返回的数据之前执行检查。function_prefix
评论
function_prefix();super_dangerous_function{window.open('youvebeenhacked!')}()
其实也不太复杂......
假设您使用的是域 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);
};
现在,当脚本加载时,它将被评估,并且您的函数将被执行。瞧,跨域请求!
还值得注意的是 JSONP 的一个主要问题:你失去了对请求的大量控制。例如,没有“好”的方法来取回正确的故障代码。结果,您最终会使用计时器来监视请求等,这总是有点可疑。JSONRequest 的主张是一个很好的解决方案,它允许跨域脚本、维护安全性并允许对请求进行适当的控制。
如今(2015 年),与 JSONRequest 相比,CORS 是推荐的方法。JSONP 对于较旧的浏览器支持仍然有用,但考虑到安全隐患,除非您别无选择,否则 CORS 是更好的选择。
评论
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 代表 带填充的 JSON。(技术名称非常糟糕,因为它实际上与大多数人认为的“填充”无关。
评论
JSONP 通过构造一个“script”元素(在 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 . ")";
?>
评论
JSONP 是解决跨域脚本错误的绝佳选择。您可以纯粹通过 JS 使用 JSONP 服务,而不必在服务器端实现 AJAX 代理。
您可以使用 b1t.co 服务来查看其工作原理。这是一项免费的 JSONP 服务,可让您缩小 URL。下面是用于服务的 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"});
因此,当它作为 src 加载到你的 js 中时,它将自动运行 whateverJavascriptName,你应该把它实现为你的回调函数:
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 用法的简单示例。
客户端.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>
服务器.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 非常复杂。它使用户不方便处理嵌入在网页中的页面。
为了使JavaScript能够方便地交换数据,甚至作为数据处理程序,我们根据JavaScript对象使用措辞,开发了一种简单的数据交换格式,这就是JSON。JSON 可以用作数据,也可以用作 JavaScript 程序。
JSON可以直接嵌入到JavaScript中,使用它们可以直接执行某些JSON程序,但由于安全限制,浏览器沙箱机制禁用了跨域JSON代码执行。
为了让JSON在执行后可以传递,我们开发了一个JSONP。JSONP 通过 JavaScript 回调功能和 < 脚本>标记绕过浏览器的安全限制。
简而言之,它解释了 JSONP 是什么,它解决了什么问题(何时使用它)。
评论
JSONP 代表 带填充的 JSON。
这是这个网站,有很好的例子,从这种技术的最简单使用到最先进的平面JavaScript的解释:
上面描述的我最喜欢的技术之一是动态 JSON 结果,它允许将 JSON 发送到 URL 参数中的 PHP 文件,并让 PHP 文件也根据它获得的信息返回一个 JSON 对象。
jQuery.ajax({
url: "https://data.acgov.org/resource/k9se-aps6.json?city=Berkeley",
jsonp: "callbackName",
dataType: "jsonp"
}).done(
response => console.log(response)
);
对于那些需要它的人来说,这是一个非常 ELI5(“像我 5 岁一样解释我”)的尝试。
TL;博士
JSONP 是一种古老的技巧,旨在绕过 Web 浏览器中的安全限制,该限制禁止我们获取与我们自己的网站/服务器不同的网站/服务器(称为不同的来源1)中的数据。
这个技巧的工作原理是使用标签从其他地方加载一个JSON(例如:),它将向我们发送包装在一个函数中的数据,即实际的JSONP(“带填充的JSON”):<script>
{ "city":"Barcelona" }
tourismJSONP({"city":"Barcelona"})
以这种方式接收它使我们能够在我们的函数中使用数据。JSONP 是一种不好的做法,不再需要了,不要使用它(在最后阅读)。tourismJSONP
问题
假设我们正在构建一个新网站,并希望在那里显示一些托管在 的 JSON 数据(或任何原始数据)。如果我们要使用 GET 请求(思考、或调用等),我们的浏览器会告诉我们不允许出现这个丑陋的错误:ourweb.com
anotherweb.com
XMLHttpRequest
fetch
$.ajax
这是一个内容安全策略限制错误,旨在保护用户免受某些攻击。您应该正确配置它(见最后)。
JSONP 技巧在这里对我们有什么帮助?好吧,标签不受整个服务器(源1)的限制!这就是为什么我们可以从任何服务器加载像jQuery或Google Maps这样的库。<script>
重要的一点是:如果你仔细想想,这些库是实际的、可运行的 JS 代码(通常是一个包含所有逻辑的庞大函数)。但原始数据不是代码。没有什么可运行的;它只是纯文本。
因此,浏览器将下载我们的标签指向的数据,并在处理时理所当然地抱怨:<script>
WTF:这是我们加载的废话吗?它不是代码。我无法计算!
{"city":"Barcelona"}
旧的 JSONP hack
我们越来越接近了,我们可以提取数据,但不能操纵/使用它。如果我们能以某种方式使纯文本可运行,我们就可以在运行时获取它。我们需要像代码一样发送它,所以当它下载到访问者的浏览器上时,就会运行它。我们只需要两件事:1)以一种可以运行的方式获取数据,2)在客户端中编写一些代码,以便在数据运行时调用我们的函数,然后我们可以使用数据。anotherweb.com
对于 1) 如果外部服务器对 JSONP 友好,我们将要求提供如下数据:
<script src="https://anotherweb.com/api/tourism-data.json?myCallback=tourismJSONP"></script>
所以我们会像这样收到它:
tourismJSONP({"city":"Barcelona"})
现在,它使它成为我们可以与之交互的 JS 代码。
根据 2),我们需要在代码中编写一个同名的函数,如下所示:
function tourismJSONP(data){
alert(data.city); // "Barcelona"
}
浏览器将下载JSONP并运行它,它调用我们的函数,其中参数将是来自的JSON数据。现在,我们可以随心所欲地处理数据。data
anotherweb.com
不要使用 JSONP,使用 CORS
JSONP 是一种跨站点黑客攻击,但有一些缺点:
- 我们只能执行 GET 请求
- 由于它是由简单的脚本标记触发的 GET 请求,因此我们无法获得有用的错误或进度信息
- 还有一些安全问题,例如在客户端 JS 代码中运行,这些代码可能会更改为恶意负载
- 它只解决了JSON数据的问题,但同源安全策略适用于其他数据(WebFonts,使用drawImage()绘制的图像/视频......
- 它不是很优雅,也不是很可读。
结论是,现在没有必要使用它。
您应该在此处阅读有关 CORS 的信息,但其要点是:
跨域资源共享 (CORS) 是一种使用 额外的 HTTP 标头,用于告诉浏览器提供 Web 应用程序 在一个源上运行,从另一个源访问选定的资源 起源。Web 应用程序在执行跨域 HTTP 请求时 请求具有不同来源(域、协议或 port)从它自己。
- 源由 3 个定义:协议、端口和主机。因此,与(不同的协议)、(不同的端口)和显然(不同的主机)的来源不同
https://web.com
http://web.com
https://web.com:8081
https://thatotherweb.net
评论
背景
您应该尽可能使用 CORS(即您的服务器或 API 支持它,并且浏览器支持是足够的),因为 JSONP 具有固有的安全风险。
例子
JSONP(带填充的 JSON)是一种常用的方法 绕过 Web 浏览器中的跨域策略。(不允许向浏览器认为位于不同服务器上的网页发出 AJAX 请求。
JSON 和 JSONP 在客户端和服务器上的行为不同。JSONP 请求不使用 和 关联的浏览器方法进行调度。而是创建一个标记,其源设置为目标 URL。然后将此脚本标记添加到 DOM(通常在元素内部)。XMLHTTPRequest
<script>
<head>
JSON 请求:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
// success
};
};
xhr.open("GET", "somewhere.php", true);
xhr.send();
JSONP 请求:
var tag = document.createElement("script");
tag.src = 'somewhere_else.php?callback=foo';
document.getElementsByTagName("head")[0].appendChild(tag);
JSON 响应和 JSONP 响应之间的区别在于,JSONP 响应对象作为参数传递给回调函数。
JSON格式:
{ "bar": "baz" }
JSONP:
foo( { "bar": "baz" } );
这就是为什么您会看到包含该参数的 JSONP 请求,以便服务器知道包装响应的函数的名称。callback
在浏览器评估标记时(请求完成后),此函数必须存在于全局范围内。<script>
处理 JSON 响应和 JSONP 响应之间需要注意的另一个区别是,JSON 响应中的任何解析错误都可以通过包装计算 responseText 的尝试来捕获 在 try/catch 语句中。由于 JSONP 响应的性质,响应中的解析错误将导致无法捕获的 JavaScript 解析错误。
这两种格式都可以通过在启动请求之前设置超时并在响应处理程序中清除超时来实现超时错误。
使用 jQuery
使用 jQuery 发出 JSONP 请求的用处在于 jQuery 在后台为您完成所有工作。
默认情况下,jQuery 要求您在 AJAX 请求的 URL 中包含。jQuery 将采用您指定的函数,为其分配一个唯一名称,并在全局范围内发布它。然后,它会将问号替换为它分配的名称。&callback=?
success
?
&callback=?
比较相似的 JSON 和 JSONP 实现
下面假定一个响应对象{ "bar" : "baz" }
JSON格式:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
document.getElementById("output").innerHTML = eval('(' + this.responseText + ')').bar;
};
};
xhr.open("GET", "somewhere.php", true);
xhr.send();
JSONP:
function foo(response) {
document.getElementById("output").innerHTML = response.bar;
};
var tag = document.createElement("script");
tag.src = 'somewhere_else.php?callback=foo';
document.getElementsByTagName("head")[0].appendChild(tag);
评论