简单的 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 时它不起作用?

如果我使用 &lt;script src="http://server/server.php?callback=process"&gt;&lt;/script&gt; 而不是 $.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 添加&lt;script&gt; 元素,这意味着响应文本永远不会被评估为 Javascript 代码 - JSONP 机制中至关重要的最后一步.


第二个版本回答起来有点棘手,并且根据提供的信息知道答案是正确的,但是对于您上面显示的 HTML 布局,我有理由相信这是原因:您的 HTML 元素定义错误顺序。

在页面加载时处理 DOM 时,处理会在每个 &lt;script&gt; 元素处暂停,同时同步加载和处理关联的 Javascript。这是为了确保所有 Javascript 都按预期的顺序执行,并且任何对以特定位置方式直接修改 DOM 的事物的调用 - 例如document.write()(您应该永远使用它,以防你不知道) - 被正确地尊重。

这样做的结果是,如果您将 &lt;script&gt; 元素放在 DOM 中 &lt;p id="result"&gt; 标签之前,则结果标签实际上并不存在于 process() 的位置函数被调用。而且因为您使用 jQuery 选择器而不是 document.getElementById(),它会吞下如果您尝试直接修改它会导致的错误。

这就是许多 Javascript 开发人员(包括我自己)现在会告诉您应该将 &lt;script&gt; 标记作为 &lt;body&gt; 中的最后一个元素的原因,因为它可以带来明显的性能改进 - 即使在实际上,加载和处理所有页面资源所花费的网络时间完全相同,它允许浏览器更快地呈现页面。这样做也不需要使用诸如$(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 示例不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Safari/iPhone 和 Opera/Android 中的 JSONP 不起作用

为啥 JsonP 不起作用? [复制]

JSONP回调不起作用

Angularjs JSONP不起作用

angularjs jsonp 在本地不起作用

带有远程 URL 的 JSONP 不起作用