为什么ajax`application / json`运行PHP代码两次?

时间:2015-01-13 14:53:20

标签: javascript php ajax json xmlhttprequest

我正在尝试将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;

现在从网络浏览器控制台(我正在使用谷歌浏览器)进行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 requestRunning curl twice in phphttps://stackoverflow.com/questions/24373623/ajax-request-help-code-gets-created-twiceAjax form submitting twice with Yii 2

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

What is the correct JSON content type?

2 个答案:

答案 0 :(得分:0)

您的javascript代码中存在一些语法错误(如缺少括号)。您还应该使用“var”来声明变量以将范围限制为函数。也许这会导致这种奇怪的行为。在更正javascript代码后,我无法重现您提到的错误=&gt;只要一个错误日志条目,无论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”请求=&gt;如果是这样,只需返回标题。

关于缓存问题:您可以通过“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;
    }
?>

答案 1 :(得分:0)

我发布了这个答案,但解决这个问题的人实际上是Roman Shtylman,请查看他今天关于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/

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