jQuery post请求中断:只有一半的post参数到达

时间:2013-06-05 07:44:47

标签: php jquery mysql ajax post

我在LAMP环境中发现了一个奇怪的现象 在前端,我用jQuery执行一个AJAX post请求:

$.post('save.php', {data1: d1, data2: d2, [...],  dataN: dN})

预先使用jQuery从网站收集变量d1dN(例如,从文本输入,textareas,复选框等)。

文件save.php将帖子参数data1带到dataN,并在一次查询中将它们保存在数据库中。

请求大约需要500毫秒,并且除非我在请求期间更改了页面(例如,通过点击链接)。

通常情况下,我希望请求被中止并被忽略(这没关系)但是(这是奇怪的行为)请求似乎已经完成但只有部分数据传播并因此得救。

这意味着,例如,php脚本仅将data1保存到data5,并将data6设置为dataN以清空。
问题似乎是由AJAX请求(不是php脚本)引起的,因为在这种情况下,字段$_POST['data6']$_POST['dataN']没有在php中设置。

所以我的问题:
为什么会发生这种情况(这是预期的行为)?
我该如何避免呢?

更新
问题既不是jQuery也不是php。 jQuery正确收集值并尝试将它们发布到php。我刚刚验证了它 - 它有效。 另一方面,php脚本处理它按预期获得的所有内容 - 它只是没有收到整个请求 所以问题必然是被中断的请求本身。与我预期它不会中止或失败不同,它仍会传输所有数据直到中断 然后php获取这个帖子数据并开始处理它 - 显然缺少一些信息。

更新2
我通过在eof之后添加参数dataN并检查它是否在php中设置来解决问题。通过这种方式,我可以确定整个请求已被传输 然而,这并不能解决我仍然不理解的问题根源 任何人都可以帮忙吗?

12 个答案:

答案 0 :(得分:5)

尝试以下操作来调试问题:

  1. 检查您的php设置中的post_max_size,并将其与您发布的数据大小进行比较。

  2. 用户HTTP请求构建器,即使用Fiddler发出http请求并检查它返回的内容。

  3. 使用print_r($_POST);顶部的save.php来检查您的内容。

  4. 使用Firebug之类的工具查看jQuery发布的内容。

  5. 您还应该验证您要发布的客户端上的json对象。即JSON.stringify(some_object);

  6. 尝试发布一些基本样本数据{ "data1":1, "data2":2, "data3":3, "data4":4, "data5":5 , "data6":6 }

  7. 很可能您发送的数据很多,或者数据可能无效!

    <强>编辑: 非常愚蠢的行为,但是让我们说你也发布了计数。所以直接检查isset($ _ POST ['data'。$ _ POST ['count']])

答案 1 :(得分:4)

您可以通过PHP验证收到的Content-Length标题的值

在运行POST查询时,此值已在客户端计算。如果它不匹配,那就是你的错误。这就是您需要的所有诊断 - 如果Content-Length与POST数据不匹配,请将POST拒绝为无效;不需要额外的参数(计算POST数据长度可能会很麻烦)。此外,你可能想调查为什么PHP,虽然解码POST ,因此能够验证它的长度,但似乎接受了错误的长度(也许检测错误所需的信息在某处$_SERVER变量?)。

如果 匹配但仍然数据未到达(即Content-Length更小,并正确描述了截止POST ),然后证明在截止后检查了,因此错误是在浏览器中(或者,不太可能,在jQuery中)或者浏览器和服务器(代理?)正在接收不完整的查询(内容长度&gt;实际长度)并且错误地重写它,使其对服务器显得“正确”,而不是拒绝它手。

对理论和变通方法的一些测试

执行摘要:前者错了,但后者显然是正确的。请参阅下面的代码,了解适用于我的测试系统的示例(Linux OpenSuSE 12.3,Apache)。

我认为错误Content-Length的请求会被400 Bad Request拒绝。我错了。似乎至少我的Apache 很多更宽松。

我使用这个简单的PHP代码来访问我感兴趣的关键变量

<?php
    $f = file_get_contents("php://input");
    print $_SERVER['CONTENT_LENGTH'];
    print "\nLen: " . strlen($f) . "\n";
?>

然后我准备了一个错误Content-Length的请求,使用nc将其发送出去:

POST /p.php HTTP/1.0
Host: localhost
Content-Length: 666

answer=42

......而且,nc localhost 80 < request没有产生400错误:

HTTP/1.1 200 OK
Date: Fri, 14 Jun 2013 20:56:07 GMT
Server: Apache/2.2.22 (Linux/SUSE)
X-Powered-By: PHP/5.3.17
Vary: Accept-Encoding
Content-Length: 12
Content-Type: text/html

666
Len: 10

在我看来,如果请求以回车结束,并且什么回车 - LF,那么内容长度可能会减少一到两个? CRLF?但是,当我添加简单的HTML以便能够从浏览器发布它时

<form method="post" action="?"><input type="text" name="key" /><input type="submit" value="go" /></form>

我能够在Firefox(最新),IE8,Chrome(最新)中验证所有在XP Pro SP3上运行, Content-Length的值与strlen的{​​{1}}相同php://input

除非请求被切断,即。

唯一的问题是php://input is not always available即使对于POST数据也是如此。

这让我们陷入了困境:

如果错误在网络级别,即POST已准备好并提供正确的内容长度,但中断使得整个数据被截断为此评论霍伦似乎表示:

  

所以只有前几个帖子参数到达,有时一个参数的值甚至在中间被中断

然后真的检查Content-Length将阻止PHP处理不完整的请求:

<?php
    if ('POST' == $_SERVER['SERVER_PROTOCOL'])
    {
            if (!isset($_SERVER['Content-Length']))
            {
                header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request', True, 400);
                die();
            }
            if (strlen(file_get_contents('php://input'))!=(int)($_SERVER['Content-Length']))
            {
                header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request', True, 400);
                die();
            }
    }
    // ... go on
?>

另一只手如果问题出现在jQuery中,即以某种方式中断阻止jQuery从汇编完整的POST,然而POST已经组成,内容-Length计算了不完整的数据,并且发送了数据包 - 然后我的解决方法不可能工作,并且必须使用“telltale”额外字段,或者......也许jQuery中的.post函数可能扩展到包括CRC字段?

答案 2 :(得分:4)

我认为我们可以排除服务器站点的问题(除非它是一些异国情调或自制的服务器守护程序),因为没有人发送&#34;数据结束&#34; - 参数HTTP POST请求以确保所有数据都已真正发送。这由HTTP本身处理(参见例如Detect end of HTTP request body)。此外,我不认为在Content-Length数据发送到您的服务器时必须检查POST标题,这完全是因为没有人这样做。至少不会像你描述它们那样完全常见的情况(通过Ajax发送POST jQuery)。

所以我认为jQuery发送语法正确的POST,但它已被切断。我的猜测是,如果您通过导航到另一个页面来中断此数据收集,jQuery会从其能够收集的数据中构建Ajax请求,并将语法正确的POST发送到你的服务器,但有数据中断。

由于您正在使用Firebug,请转到其net标签并激活persist,以便在导航到其他网页时不会丢失流量数据。然后触发您的Ajax POST,导航到另一个页面(从而&#34;中断&#34; Ajax来电)并在Firebug的网络标签中查看哪些数据有实际上是通过打开所有 POST个请求并检查Headers标签(以及Request Headers标签内)来发送到服务器。

我的猜测是可能发生以下两种情况之一:

  1. 您会看到发送到服务器的数据已在Firebug net标签中显示的标题中已被切断,并且Content-Length已计算根据{{​​1}}数据的实际(截止)长度正确。否则,我确定服务器会拒绝该请求为POST整体。
  2. 您会看到有多个Bad Request请求,其中一些(可能包含完整的非切断数据)实际上已中断,因此从未到达服务器,但至少有一个POST请求(再次,使用截断数据)由POST中的其他机制触发,即不是您想到的触发器,而是导航到另一个页面,更多和其他{可能会触发{1}}请求(因为我不知道您的源代码,所以只是一个猜测)。
  3. 在任何一种情况下,我认为你会发现这个问题与客户端有关,服务器只处理客户端发送到的(不完整的,但是(在Javascript)语法上有效的数据)它

    从那时起,您可以调试Ajax并实施一些防止向服务器发送不完整数据的机制。同样,由于我不知道你的其他源代码,但很难说清楚该做什么,但也许在收集数据时会有一些重大的动作,你可能会确保只有在真正收集到所有数据时才会发生HTTP。或者,也许您可​​以阻止导航,直到Javascript请求完成或此类事情。

    有趣的是,如果所有这些都没有意义,那就是查看更多的源代码,特别是如何触发POST以及是否还有其他事件等等,如果你导航到另一个页面。您发送的示例数据也很有趣。

    编辑:我还想指出,使用Ajax输出数据可能会产生误导,因为它并不能保证这是实际发送的数据,它是&#39 ; s只是一个日志,它在调用Ajax POST的确切时间评估给定输出。这就是为什么我建议嗅探网络流量的原因,因为那时(并且只有这样)你可以确定发送(和接收)真正的是什么。 尽管如此,如果您不习惯这种情况,这有点棘手(如果您使用加密流量,例如使用console.log())则不可能,因此console.log() HTTPS标签可能是一个很好的妥协。

答案 3 :(得分:3)

Post数据看起来就像GET

Header1: somedata1\r\n
Header2: somedata2\r\n
...
HeaderN: somedataN\r\n
\r\n
data1=1&data2=2&...&dataN=N

当请求中止时,在某些情况下,最后一行可能只是部分传递。所以,这里有一些可能的解决方案:

  1. 比较Content-Lengthstrlen($HTTP_RAW_POST_DATA)
  2. 验证输入数据
  3. 一次传递的数据不多

答案 4 :(得分:3)

我尝试使用触发器手动重新创建此问题,更改服务器设置,尽我所能#$%&amp;事情,使用不同的数据大小,但我从来没有得到PHP的一半请求。只是因为apache不会调用PHP直到请求完全完成。见this question about Reading “chunked” POST data in PHP

所以唯一可能出错的是Jquery只收集部分数据然后发出POST请求。正如您所提到的那样只使用$.post('save.php', data),这不会发生。它要么正在收集数据,要么等待服务器的响应。

如果您在聚会期间切换网站,则不会有请求。如果您在请求完成后切换并快速离开,则在传输所有数据之前,apache服务器会将其视为请求的一半,并且不会调用PHP。

一些建议:

您是否有可能使用单独的页面来获取成功和部分请求?因为PHP只将前1000个元素添加到$_POST,并且失败的请求可能包含更多data1000=data个元素?所以不会有EOF参数。

您是否有可能在javascript中的全局var中收集数据,并且还有一个onbeforeunload方法也可以发送数据?因为那时POST中可能只有一半的数据。

您可以分享一些有关您正在发送的数据的信息吗?有很多小元素(比如data1到data10000)还是一些大的元素?

它总是与你上次收到的元素相同吗?像你提到的那样总是像data6一样?因为如果是这样,尝试失败的可能性总是在完全相同的dataN字段上会非常小。

答案 5 :(得分:2)

您可以在

查看主机日志吗?

/var/log/messages

上次我在php上“丢失”了帖子变量我发现我发送了空的ASCII字符,而服务器(CentOS)正在考虑它是一种攻击,然后丢弃那些特定的变量......花了我一个星期才算出来出来了!这是服务器日志响应:

suhosin[1173]: ALERT - ASCII-NUL chars not allowed within request variables - dropped variable 'data3' (attacker '192.168.0.37', file '/var/www/upload_reader.php')

如果这是您的问题,请使用js压缩变量,使用base64对它们进行编码。用ajax发布它们,然后在php接收,解码64然后解压缩!这解决了我;)

答案 6 :(得分:1)

为了更多地了解正在发生的事情,为什么不使用jQuery的ajax函数正确编码ajax请求。使用所有回调函数来跟踪您的通话或回来的内容?元素type设置为POST,元素data包含您喜欢的任何对象结构{ ... }

$.ajax({
    url : "save.php",
    type : "POST",
    data : {
        "ajax_call" : "SOME_CUSTOM_AJAX_REQUEST_REFERENCE",
        "data1" : data1,
        "data2" : data2,
        "data2" : data2,
        "dataN" : dataN
    },
    //dataType : "html", contentType: "text/html; charset=utf-8",
    dataType : "json", contentType: "application/json; charset=utf-8",
    beforeSend: function () {
        //alert('before send...');
    },
    dataFilter: function () {
        //alert('data filter...');
    },
    success: function(data, textStatus, jqXHR) {
        //alert('success...');
        var response = JSON.parse(jqXHR.responseText, true);
        if (undefined != response.data) {
            my_error_function();
        }
        my_response_function(response.data);
    },
    error: function(jqXHR, textStatus, errorThrown) {
        //alert('error...');            
    },
    complete: function (xhr, status) {
        //alert('end of call...');          
        my_continuation_function();
    }
});

答案 7 :(得分:1)

在发送请求之前,为文档设置“onbeforepageunload”处理程序(禁止转换到另一个页面),并在成功后取消绑定。

例如:

$(document).on('unload', function(e){
    e.preventDefault();
    // Here you can display a message, you need to wait a bit
    return false;
});

答案 8 :(得分:1)

猜测 - 你的Ubuntu / Debian服务器上的suhosin函数会导致字段被切断吗?

答案 9 :(得分:1)

我的问题是我的一个帖子对象中有太多变量。 PHP有一个max_input_vars变量,默认设置为1000。

我将此行添加到我的.htaccess文件中(因为我无法访问php.ini文件):

php_value max_input_vars 5000

问题解决了!

答案 10 :(得分:0)

我有同样的问题。 POST大小约为650KB,如果在ajax的情况下关闭浏览器窗口或刷新页面,则在帖子完成之前会损坏。 ajax.success()未被触发,但部分数据以200 OK状态发布到“save.php”。 $ _SERVER的内容长度与建议的elswhere没有用,因为它与部分数据的实际内容长度相匹配。

我想出了两种克服这种方法的方法:

  1. 按照你的建议,在末尾添加一个post变量。似乎工作,虽然看起来有点冒险。
  2. 将你的“save.php”脚本保存到临时db列,然后使用ajax.success()调用另一个php脚本,比如savefinal.php,而不传递任何数据,这些数据从temp列传输到最后一列(或只是将此数据标记为有效)。这样,如果post被中断,数据将只驻留在数据库的temp列上(或者不会被标记为有效)。
  3. 如果post被中断,则不会调用.success(),所以这应该有用。

    我认为这是一个jquery错误,向apache发送错误的(非常小的)内容长度,并且apache被迫假设发布请求已经完成,但我不确定。

答案 11 :(得分:0)

为什么会发生这种情况(这是预期的行为)?

当帖子中的某些变量丢失时,也在寻找这种奇怪行为的原因,并且在这个问题中解释了非常相似的行为 ,因为Web客户端发送POST请求的连接速度很慢with multipart / form-data

以下是提到的问题:

  

当连接速度较慢的远程Web客户端时,我遇到了问题   无法使用multipart / form-data内容发送完整的POST请求   但PHP仍然使用部分接收的数据来填充$ _POST数组。   因此,$ _POST数组中的一个值可能不完整而且更多   值可能会丢失。

请参阅How to check for incomplete POST request in PHP

我该如何避免呢?

在那里你也可以找到推荐的解决方案。尽管如此,您已经提出了相同的解决方案。

  

您可以添加字段<input type="hidden" name="complete">(例如)   作为最后参数。在PHP中首先检查此参数是否为   从客户发送。如果发送此参数 - 您可以确定   获得了整个数据。