为什么在Ajax应用程序`/ json`运行PHP code两次?

问题描述:

我想张贴JSON数据从JavaScript到PHP。你可以做到这一点无论是

I am trying to post JSON data from JavaScript to PHP. You can do that with either

Content-Type: application/json

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

这两部作品,但我有一个很难得到的第一个工作。原因是,我错过了PHP脚本似乎运行的2倍时,内容类型为应用程序/ JSON

我很惊讶,不知道是怎么回事。任何人能解释这是怎么回事,如何处理呢? (有一些相关的问题,但没有答案似乎来观察这种行为。)

I am quite surprised and wonder what is going on. Can anyone explain what is going on and how to handle it? (There are some related questions, but none of the answers seems to observe this behaviour.)

下面是我的测试code。首先启动PHP内置的服务器:

Here is my test code. First start PHP builtin server:

php.exe -S localhost:7000

然后在该目录下创建文件 test1.php 具有以下code:

Then in that directory create the file test1.php with the following code:

<?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;

现在从Web浏览器的控制台(我使用谷歌浏览器),从AJAX请求:

Now from a web browser console (I am using Google Chrome) make an ajax call:

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) 

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

The output in the console is just what I expect:

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

现在,而不是做 applicaion / JSON ,即 test1的Ajax调用(真)。输出目前是:

Now instead do the ajax call with applicaion/json, i.e. test1(true). The output is now:

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

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

正如你所看到的PHP code已被运行两次。并在第一时间没有输入对 PHP://输入

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

Why??? And how is this supposed to be handled in PHP?

借用一些code在这里:Sending一个JSON服务器和检索一个JSON作为回报,不JQuery的

Borrowed some of the code here: Sending a JSON to server and retrieving a JSON in return, without JQuery

下面是一些相关的问题: code将在Ajax请求运行两次一>, 运行卷曲在PHP 两次, code被创建Ajax请求帮助的两倍, 阿贾克斯与形式的Yii 2 提交两次

Here are some related questions: Code to be run twice in Ajax request, Running curl twice in php, ajax request help Code gets created twice, Ajax form submitting twice with Yii 2

和这个问题(这里的最高投票的)当然是相关的,但有什么可说的,为什么PHP code运行两次:

And this question (one of the highest voted here) is of course relevant, but has nothing to say about why the PHP code is run twice:

什么是正确的JSON内容类型?

您在您的JavaScript code有一些语法错误(如缺少括号内)。你也应该使用无功的声明你的变量来限制范围的功能。也许这会导致奇怪的行为。修正后的JavaScript code我无法重现您提到的错误=>只有一个错误日志条目无论对于test1的参数设置为true或false。

You had some syntax errors in your javascript code (like missing brackets). Also you should use "var" for declaring your variables to limit the scope to the function. Maybe that causes that strange behavior. After correcting the javascript code I couldn't reproduce the error you mentioned => only one error log entry no matter if the parameter for test1 is set to true or false.

下面是更正后的code - 试试看:

Here is the corrected code - give it a try:

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文件嵌入它。所以它不是一个跨域请求,这就是为什么它为我工作的原因。

Ah, sorry my bad. I didn't read that you run the script from the javascript console. I embedded it in an php file that I called via the browser. So it wasn't a cross domain request and that's the reason why it worked for me.

我现在能够重现你的问题。正如你所提供的链接说明,你应该检查一下请求的HTTP方法是一个选项请求=>如果这样,只返回头。

I was able to reproduce your problem now. As stated in the link provided by you, you should check if the HTTP Method of the request is an "OPTIONS" request => if so, just return the headers.

关于缓存的问题:您可以通过访问控制-max-age的头设置preflight缓存岁零

Concerning the caching issue: You can set the preflight caching age to zero via the "Access-Control-Max-Age" header.

下面是整个PHP code应该是:

Here is the whole php code that should work:

<?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;
    }
?>