【问题标题】:Why do an ajax `application/json` run PHP code twice?为什么 ajax `application/json` 会运行 PHP 代码两次?
【发布时间】:2015-03-11 13:43:28
【问题描述】:

我正在尝试将 JSON 数据从 JavaScript 发布到 PHP。你可以这样做

Content-Type: application/json

Content-Type: application/x-www-form-urlencoded

两者都有效,但我很难让第一个工作。原因是我错过了内容类型为application/json时PHP脚本似乎运行了2次。

我很惊讶,想知道发生了什么事。谁能解释发生了什么以及如何处理它? (有一些相关的问题,但似乎没有一个答案观察到这种行为。)

这是我的测试代码。首先启动PHP内置服务器:

php.exe -S localhost:7000

然后在该目录中使用以下代码创建文件test1.php

<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Content-Type");
header("Content-Type: application/json");

function writeStdErr($msg) { $fh = fopen('php://stderr','a'); fwrite($fh, $msg); fclose($fh); }
writeStdErr("\n\nI am here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");

$e = stripslashes(file_get_contents("php://input"));
writeStdErr("e=".$e."\n");
if (strlen($e) > 0) echo $e;

现在从网络浏览器控制台(我使用的是 Google Chrome)进行 ajax 调用:

function test1(j) {
    url = "http://localhost:7000/test1.php";

    type = "application/x-www-form-urlencoded";
    if (j) type = "application/json";

    xhr = new XMLHttpRequest();
    xhr.open("POST", url, true);
    xhr.setRequestHeader("Content-type", type);
    xhr.addEventListener("readystatechange", function () { 
        if (xhr.readyState == 4 && xhr.status == 200) {
            var json = JSON.parse(xhr.responseText);
            console.log(json.email + ", " + json.password)
        }
    });
    var data = JSON.stringify({"email":"hey@mail.com","type":type});
    xhr.send(data);
    console.log("now sending >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", url, type);
}
test1(false) 

控制台中的输出正是我所期望的:

I am here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
e={"email":"hey@mail.com","type":"application/x-www-form-urlencoded"}

现在改为使用applicaion/json 进行ajax 调用,即test1(true)。现在的输出是:

I am here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
e=

I am here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
e={"email":"hey@mail.com","type":"application/json"}

如您所见,PHP 代码已经运行了两次。并且第一次在php://input上没有输入。

为什么???这应该如何在 PHP 中处理?


这里借用部分代码:Sending a JSON to server and retrieving a JSON in return, without JQuery

以下是一些相关问题: Code to be run twice in Ajax request, Running curl twice in php, https://stackoverflow.com/questions/24373623/ajax-request-help-code-gets-created-twice, Ajax form submitting twice with Yii 2

而这个问题(这里投票最高的问题之一)当然是相关的,但对于 PHP 代码为什么会运行两次没有什么可说的:

What is the correct JSON content type?

【问题讨论】:

    标签: javascript php ajax json xmlhttprequest


    【解决方案1】:

    您的 javascript 代码中有一些语法错误(例如缺少括号)。此外,您应该使用“var”来声明变量以限制函数的范围。也许这会导致这种奇怪的行为。更正javascript代码后,我无法重现您提到的错误=>无论test1的参数设置为true还是false,都只有一个错误日志条目。

    这是更正后的代码 - 试一试:

    function test1(j) {
        var url = "http://localhost:7000/test1.php";
        var type = "application/x-www-form-urlencoded";
    
        if (j) {
            type = "application/json";
        }
    
        var xhr = new XMLHttpRequest();
        xhr.open("POST", url, true);
        xhr.setRequestHeader("Content-type", type);
        xhr.addEventListener("readystatechange", function () { 
                if (xhr.readyState == 4 && xhr.status == 200) {
                        var json = JSON.parse(xhr.responseText);
                        console.log(json.email + ", " + json.type)
                }
        });
        var data = JSON.stringify({"email":"hey@mail.com","type":type});
        xhr.send(data);
        console.log("now sending >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", url, type);
    }
    test1(false);
    

    编辑:

    啊,对不起,我的错。我没有读到您从 javascript 控制台运行脚本。我将它嵌入到我通过浏览器调用的 php 文件中。所以这不是跨域请求,这就是它对我有用的原因。

    我现在能够重现您的问题。如您提供的链接中所述,您应该检查请求的 HTTP 方法是否是“OPTIONS”请求 => 如果是,则只需返回标头。

    关于缓存问题:您可以通过“Access-Control-Max-Age”标头将预检缓存年龄设置为零。

    这是应该工作的整个 php 代码:

    <?php 
        header("Access-Control-Allow-Origin: *");
        header("Access-Control-Allow-Headers: Content-Type");
        header("Content-Type: application/json");
        header("Access-Control-Max-Age: 0");
    
        if ($_SERVER['REQUEST_METHOD'] != 'OPTIONS') {
            function writeStdErr($msg) {
                $fh = fopen('php://stderr','a'); 
                fwrite($fh, $msg); 
                fclose($fh); 
            }
    
            writeStdErr("\n\nI am here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
    
            $e = stripslashes(file_get_contents("php://input"));
            writeStdErr("e=".$e."\n");
            if (strlen($e) > 0) echo $e;
        }
    ?>
    

    【讨论】:

    • 谢谢,但这对我来说完全一样。你用的是什么PHP?还有什么浏览器? (我的代码对你来说真的有不同的工作方式吗?)
    • PHP 5.4.35、Firefox 33.1.1 和 LAMP。我也像您一样使用内置服务器进行了尝试-仍然无法重现。使用 Firefox 是否也有同样的行为?
    • 是的,我对 FF 也有同样的行为。在 Windows 7 上。
    • 但我认为问题解决了。请看我的回答。
    • 查看我的更新答案。您是否真的需要跨浏览器请求,或者它只是您通过 javascript 控制台测试脚本时的副作用?最终谁会消费你的服务?例如,如果您想将您的 javascript 集成到另一个网站中(这样他们就可以使用您的服务),那么 CORS 就很有意义。
    【解决方案2】:

    我发布了这个答案,但解决这个问题的人实际上是 Roman Shtylman,请参阅他的 cmets 关于 CORS 预检here 今天(2015-01-13)。

    如果你使用application/x-www-form-urlencode,规则似乎是没有CORS预检,但如果你使用application/json,则有:

    https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

    因此,如果使用一些人建议的“错误”标题(即application/x-www-form-urlencode),则可能会避免往返:

    https://remysharp.com/2011/04/21/getting-cors-working

    但是...然后有缓存:

    http://www.html5rocks.com/en/tutorials/cors/

    如果缓存真的发生是另一个问题。我完全不知道这个问题的答案。 :-(

    【讨论】:

      猜你喜欢
      • 2013-07-05
      • 1970-01-01
      • 2019-12-04
      • 2019-09-06
      • 2015-05-20
      • 1970-01-01
      • 1970-01-01
      • 2017-04-20
      • 1970-01-01
      相关资源
      最近更新 更多