简单的 JSONP 和 PHP 示例不起作用
Posted
技术标签:
【中文标题】简单的 JSONP 和 PHP 示例不起作用【英文标题】:simple JSONP & PHP example not working 【发布时间】:2013-08-02 05:53:53 【问题描述】:一直在尝试创建一个简单的 JSONP 调用,但它并不总是有效,我不知道为什么。代码如下:
服务器端(http://server/server.php
):
<?php
$res = json_encode("It works!");
if(isset($_GET['callback']) === TRUE)
header('Content-Type: text/javascript;');
header('Access-Control-Allow-Origin: http://client');
header('Access-Control-Max-Age: 3628800');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
echo $_GET['callback']."(".$res.");";
else
echo $res;
?>
客户端(http://client/client.html
):
<html>
<head><title>JSONP</title></head>
<body>
<h1>JSONP Experiment</h1>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
function process(data)
$('#result').text(data);
$.getJSON(
'http://server/server.php?callback=?',
'callback': 'process'
);
</script>
<p id="result"></p>
</body>
</html>
此代码有效并显示“有效!”在我的
阻止。
为什么我不使用 'callback': 'process'
并将 ?callback=process 直接放入 $.getJSON() URL 时它不起作用?
如果我使用 <script src="http://server/server.php?callback=process"></script>
而不是 $.getJSON() 调用,为什么它不起作用?
两个不工作的情况实际上都返回 process("It works");
但这没有被执行,为什么?
谢谢
【问题讨论】:
请注意,echo $_GET['callback']
确实不是一个好主意。你应该确保函数名是一个有效的 JS 标识符。
@lonesomeday 虽然它并没有真正构成安全风险。如果代码发送的东西不是有效的 JS 标识符,那么将会发生的一切就是它根本不起作用——唯一可以实现的就是破坏它,这只会影响调用客户端,所以基本上,攻击者只会把自己搞砸。
@DaveRandom 这是一个潜在的 XSS 漏洞。更重要的是,应该使用错误响应来处理故障事件。在这种情况下,400 Bad Request
。但这是问题的一个侧面。
@lonesomeday 事实上,它是直接(而不是存储)回显,结合 SOP,应该 否定 XSS 漏洞(除非我遗漏了什么?)但是我完全同意处理错误。
【参考方案1】:
这里有两个不同的问题,有不同的答案
当您使用 $.getJSON()
并将回调的名称按字面意思放在 URL 中时它不起作用的原因是 jQuery 的工作方式。
首先让我们看看 jQuery 如何检测到您的 $.getJSON()
调用期待 JSONP,如果您没有在设置对象中传递任何明确指示它的选项 - 它是 uses 这个正则表达式:
/(=)\?(?=&|$)|\?\?/
这会显式查找查询字符串中的 =?
或仅由单个 ?
组成的查询字符串 - 本质上,检测 URL 将返回 JSONP 需要问号。
没有它,jQuery 会使用 XHR 发出 Ajax 请求,并且服务器会返回正确的数据。接下来会发生什么取决于同源策略。如果像您显示的代码一样,服务器通过Access-Control-*
标头指示允许请求,则数据将可以访问,就像它通过对客户端原始服务器的 Ajax 请求一样。如果这些标头不存在,则客户端代码将无法访问返回的数据。
但是,至关重要的是,因为它只是发出一个标准的 Ajax 请求并且没有向 DOM 添加<script>
元素,这意味着响应文本永远不会被评估为 Javascript 代码 - JSONP 机制中至关重要的最后一步.
第二个版本回答起来有点棘手,并且根据提供的信息知道答案是正确的,但是对于您上面显示的 HTML 布局,我有理由相信这是原因:您的 HTML 元素定义错误顺序。
在页面加载时处理 DOM 时,处理会在每个 <script>
元素处暂停,同时同步加载和处理关联的 Javascript。这是为了确保所有 Javascript 都按预期的顺序执行,并且任何对以特定位置方式直接修改 DOM 的事物的调用 - 例如document.write()
(您应该永远使用它,以防你不知道) - 被正确地尊重。
这样做的结果是,如果您将 <script>
元素放在 DOM 中 <p id="result">
标签之前,则结果标签实际上并不存在于 process()
的位置函数被调用。而且因为您使用 jQuery 选择器而不是 document.getElementById()
,它会吞下如果您尝试直接修改它会导致的错误。
这就是许多 Javascript 开发人员(包括我自己)现在会告诉您应该将 <script>
标记作为 <body>
中的最后一个元素的原因,因为它可以带来明显的性能改进 - 即使在实际上,加载和处理所有页面资源所花费的网络时间完全相同,它允许浏览器更快地呈现页面。这样做也不需要使用诸如$(document).ready()
/DOMContentLoaded
事件来延迟执行(顺便说一句,这也可以解决这个问题,但方式稍微麻烦一些)。
【讨论】:
感谢您的详细回答。我现在更好地理解了 getJSON() 的工作原理。所以我想我的使用是正确的回调=?和一个“回调”参数设置为我想调用的任何函数。此外,我通常将 JS 脚本放在 HTML 正文的末尾,或者至少使用 $(document).ready(),但这次我采用了另一种方式。我也尝试先使用 document.getElementById() ,但没有意识到为什么我得到了一个 NULL 对象。我猜这里太热了,我的大脑出了问题。无论如何,再次感谢。 我更新了我的代码并将放在了我的
@gael JSONP 的使用不需要Access-Control-Allow-Origin:
标头,这些用于允许常规 Ajax 请求(在支持它的浏览器中)。 Content-Type:
标头绝对应该包含在内,仅供参考,JSON 的正确媒体类型是(最终标准化时)application/json
。
@gael 如果这个答案对你有帮助,请不要忘记upvote it 和accept it。
@DaveRandom 很公平。我关于 JSON/JS 的问题是关于内容类型的 header()。我想知道,当它返回对 JS 函数的调用时,它应该是 application/json 还是 text/javascript?以上是关于简单的 JSONP 和 PHP 示例不起作用的主要内容,如果未能解决你的问题,请参考以下文章