【问题标题】:Handling retry post requests in php, when both post requests reach the server当两个 post 请求都到达服务器时,在 php 中处理重试 post 请求
【发布时间】:2016-06-01 11:37:45
【问题描述】:

我的 php 应用程序的用户有时会无意中两次提交消息或其他操作。这似乎是由(移动)客户端在响应时间过长时自动重试请求引起的问题,例如由于网络连接不好。我的服务器虽然同时处理请求并将它们作为单独的操作进行处理。

如何在不禁用发送故意双重发布请求的可能性的情况下处理此类重试发布请求?因此,我如何区分这些重试和故意用户发送相同请求的重发?

一些上下文,可以帮助理解问题:我正在运行一个老式的基于 html 的浏览器游戏。用户可能想通过页面刷新故意多次重新发送完全相同的动作,如“攻击”,我不想取消这个选项,因为它真的很方便。但是我想通过客户端自动发送两次发布请求而不先询问用户来防止意外的双重操作。我不完全确定它是否是特定于移动浏览器的问题。

【问题讨论】:

  • 客户端端可能有一点 Javascript?
  • 想到的第一个解决方案是使用会话变量。可能设置最后一次发布时间,如果太早,你不允许他们再做一次。似乎是最简单的解决方案。
  • 好的,我明白了。你读过这篇非常有启发性的文章吗? stackoverflow.com/questions/14302512/…
  • @ckimbrell 是的,只需刷新并再次发送是可以接受的,并且在游戏中的很多情况下都非常方便(例如,你用你的单位填写攻击表格并想要攻击目标 3 次那个组成)。至少 Chrome 和 FF 会提示您确认您确实要重新发送,因此意外的用户重新发送不是问题。虽然是意外的客户端重试。
  • 是的,我确实理解这一点,但是如果每次提交都被保留到用户确认他们打算这样做之前,那将无济于事 - 所以如果它是提交 #1 而你突然看到提交 #3您可以防止#3 产生影响(假设您曾经收到“实际”提交!)。 stackoverflow.com/questions/15155014/… 与上面提到的优秀文章类似。我很想知道,如果你将我的链接中的代码扔到一个 php 页面中,它会在你的 Android 浏览器中返回什么值

标签: php http


【解决方案1】:

我相信这应该做你想做的事,它至少应该给你一个想法 - 它最初是关于防止实际重复提交的更大帖子 Stop multiple submits for a html from 的一部分。

再看一遍,希望你能对自己提出太多要求,所以这里有一个被替换的版本。您可能想要重新实现提交按钮的禁用,但您似乎确实希望它连续启用,因此为了清楚起见,我在此处删除了该代码。

您可能需要调整计数 - 不确定可能会发生多少次自动提交 - 就目前而言,这将占一个,但在那之后它不会接受任何东西,直到下一次“真正的”提交。

此时,PHP 代码可以请求用户干预,如“您真的提交了两次吗?”或者它可以简单地忽略提交。 JavaScript 也是如此,根据@symcbean 的建议,它可以在重新提交之前要求确认 - 皮带和大括号总是一个好主意!如果您在提交时单击,您应该可以选择故意提交两次,或者取消。

据我所知,在这个版本中,如果您注释掉 keyPress 事件 submit_this(); 调用 F5 按键,它将不接受 F5 提交以进行刷新。 - 当 keyPress 功能运行时,按 F5 提交 不会出现刷新时通常会收到的确认警告。

我已经对此进行了测试,但只能等待您的确认,看看它是否也能处理自动重新提交。如果单击浏览器刷新按钮,则 PHP 不接受提交。由Ctrl+R 刷新产生的提交也不被接受。希望这反映了重新提交的情况。由于无法判断确认/取消是否有效,我已将警报恢复为比提交页面更快的点击。你可以尝试一下!

<?php   
session_start();
if (!isset($_POST['hidden_input'])) $hidden_input = 1;
if (!isset($_SESSION['submit_count_input'])) $_SESSION['submit_count_input'] = -1;
if (isset($_POST['hidden_input'])) $hidden_input = intval($_POST['hidden_input']);

// detect difference between JavaScript submitted value and possible automatic ones
if (($hidden_input - $_SESSION['submit_count_input']) == 1){
    // do your processing here 
    //$submit_count_input should always be 1 behind
    echo "Thank you. Form processed";
    }else{
    // correct the balance between $hidden_input and $submit_count_input
    $_SESSION['submit_count_input'] = ($hidden_input - 1); // will be incremented so will be -1 effectively
    echo "Form not processed.";
}    
// remove this echo section - just for testing
echo " Control Token: " . $hidden_input . " ";
echo " Submit Count: " . $_SESSION['submit_count_input']; // to show it submits but does not process form

$_SESSION['submit_count_input']++; 
?>
<!DOCTYPE html>
  <html>
    <head>
    <script language="javascript">
    document.onkeydown=function(e) {
        var event = window.event || e;
        if (event.keyCode == 116) {
        event.keyCode = 0;
           submit_this();
           return false;
        }
    }

function submit_this(){
    if(document.getElementById('submit_count_input').value - document.getElementById('hidden_input').value == -1) {
        // Warning when submit clicked before previous submission finished confirm/cancel option as per link below could be used
    alert("Easy Tiger - please slow down a bit!");
    }else{
    document.getElementById('hidden_input').value = 1+parseInt(document.getElementById('hidden_input').value);
    document.form.submit();
    }
}
    </script>
    </head>
    <body>

      <form name="form" id="form" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']) ?>" method="post">   
      <input type="hidden" name="hidden_input" id="hidden_input" value="<?php echo $hidden_input ?>" />
      <input type="hidden" name="submit_count_input" id="submit_count_input" value="<?php echo intval($_SESSION['submit_count_input']) ?>" />

      <input type="button" name="sbutton" id="sbutton" value="Submit" onclick="submit_this();" />
      </form>

      <a href="<?php echo htmlspecialchars($_SERVER['REQUEST_URI']); ?>">Click to start a new submission</a>

    </body>
   </html>

确认/取消 JavaScript 对话框JavaScript confirm cancel button not stopping JavaScript

关于保留提交表单中的值的次要问题,没有会话,那么即使对于很多表单,如果您在每个表单文本框元素的值中使用value="&lt;?php echo htmlspecialchars($_POST['form_element_name_value'] ?&gt;",这应该是相当简单的。确保每个文本框都有一个 name="something" 供 php 提取为 $_POST - 抱歉,如果这太明显了。

为了满足您的用户能够使用 F5 提交页面的愿望,您可以捕获功能并将其颠覆为实际提交,但这可能是一个令人讨厌的想法,尽管它确实有效......而且它会保持 PHP 端提供已提交的数据,这些数据仍可能与失败连接的自动重新提交区分开来。

    document.onkeydown=function(e) {
        var event = window.event || e;
        if (event.keyCode == 116) {
        event.keyCode = 0;
        submit_this();
        return false;            }
    }

要捕获浏览器上的实际刷新按钮,您可以查看How to know whether refresh button or browser back button is clicked in firefox,但颠覆浏览器行为以强制提供给定的用户体验通常是不受欢迎的。

【讨论】:

  • 嘿,感谢您的详细回答。我在家里出了点意外,接下来几天我无法测试这是否有效。但我一定会回来并感激不尽!
【解决方案2】:

您将注意力集中在问题的错误端。如果用户两次发送帖子,则可能是因为他们没有看到最后的回复。是的,您仍然需要计划和处理服务器端的问题,但在客户端解决它不仅会减少返回服务器的发生次数,而且还会改善用户体验。

前端的实时更新意味着 JavaScript。此外,当您使用触控设备时,默认情况下会有大约三分之一秒的延迟,除非您明确禁用某些触控功能。

【讨论】:

  • 不是用户发送了两次post请求,而是他们的客户端自动发送了第二次请求。我再次更改了问题以更清楚
  • 你所说的不太可能。
  • 这种自动重试行为实际上是标准的,并且在 HTTP/1.1 RFC 8.2.4 中进行了描述,因为人们在 cmets 中链接了我。尽管对于移动客户端,两个 post 请求实际到达服务器并得到处理的次数似乎很明显。
  • @dfherr 不过这是在正确的轨道上,并且在您的“用户体验”方面非常值得考虑,特别是如果存在移动连接速度慢的已知问题。甚至可能是“易虎,等一下!”如果您检测到它尚未提交,请发出警报!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-11
  • 2011-06-27
相关资源
最近更新 更多