在尝试阻止开机自检刷新双重提交时诊断“无法修改标头信息 - 标头已发送”错误


Troubleshooting "Cannot modify header information - headers already sent" error while trying to prevent POST refresh double-submissions

我试图防止在用户刷新页面时多次提交表单。 这是我的代码:

<form action="#" method="post">
                    <table border="0">
                        <tr>
                            <td id="tb-w_a"><p>Kategori</p></td>
                            <td>:</td>
                            <td>
                            <select name="kategori">
                            <?php
                            $query = "SELECT id, title FROM forum_kategorier";
                            $result = $mysqli->query($query);
                            while(list($id, $title) = $result->fetch_row())
                            {
                                echo "<option value='"$id'">$title</option>";  
                            }
                            ?>
                            </select>
                            </td>
                        </tr>
                        <tr>
                            <td id="tb-w_a"><p>Title</p></td>
                            <td>:</td>
                            <td><input type="text" name="title"></td>
                        </tr>
                    </table>
                <textarea name="tekst_indhold" style="width:716px; height:170px;"></textarea><br />
                <input type="hidden" value="<?php echo $_SERVER['PHP_SELF'] ?>" name="return_page">
                <input type="submit" value="Opret indhold" name="godkendt">
            </form>
            <?php
            if ($stmt = $mysqli->prepare('INSERT INTO `forum_traede` (`kategori_id`, `bruger_id`, `tekst`, `dato`, `titel`) VALUES (?, ?, ?, NOW(), ?)')) { 
                $stmt->bind_param('ssss', $katgori_id, $bruger_id, $tekst, $titel);
                $katgori_id = $_POST["kategori"];
                $bruger_id = $_SESSION["user_id"];
                $tekst = $_POST["tekst_indhold"];
                $titel = $_POST["title"];
                $stmt->execute();
                $stmt->close();
                if (isset($_POST['return_page'])) {
                    $return_page = $_POST["return_page"];
                } else {
                    header('Location: http://xxxx.dk/bruger-forum-opret/');
                }
            } else {
                echo 'Der opstod en fejl i erklæringen: ' . $mysqli->error;
            }
        ?>

提交表单后,我收到以下错误:

警告:无法修改标头信息 - 标头已由/home/jesperbo/public_html public_html/xxx.dk/xxx.php:27 在第

78 行的/home/jesperbo//xxx.dk/xx.php x:27 中发送

这将显示在页面顶部。

我还尝试将表单处理代码移动到单独的.php文件中,但随后出现此错误:

致命错误:在第 2 行的/home/jesperbo/public_html/xxx.dk/bruger_forum-indhold-godkendt.php 中的非对象上调用成员函数 prepare()

提交表单后,如何将浏览器重定向到新页面?

简单的答案:把所有的PHP代码放在所有的HTML代码之前。

首先解决第二个问题:

致命错误:在第 2 行的/home/jesperbo/public_html/xxx.dk/bruger_forum-indhold-godkendt.php 中的非对象上调用成员函数 prepare()

您的脚本中的代码似乎比您发布的要多;有几个对$mysqli变量的引用,我们在您的问题中没有看到其初始化。

如果仅将表单处理代码移动到单独的文件中,但不包括定义$mysqli变量的代码,则会遇到上述错误:您正在尝试调用 $mysqli->prepare() ,但尚未定义$mysqli


现在第一个问题:

警告:无法修改标头信息 - 标头已由/home/jesperbo/public_html public_html/xxx.dk/xxx.php:27 在第

78 行的/home/jesperbo//xxx.dk/xx.php x:27 中发送

有几个问题导致了这个问题。 我们将一次解决一个问题。

1. 即使表单尚未提交,表单处理代码也会执行。

至少,您应该检查以确保在开始处理表单之前已提交表单。 考虑如下情况:

<form action="#" method="post">
  ...
</form>
<?php
  /* Check if the form was submitted. */
  if( $_POST ) {
    /* @todo Validate incoming values. */
    /* If all values are valid, insert into the database. */
    if ($stmt = $mysqli->prepare('INSERT INTO `forum_traede` (`kategori_id`, `bruger_id`, `tekst`, `dato`, `titel`) VALUES (?, ?, ?, NOW(), ?)')) {
      ...
    } else {
      ...
    }
  }

2. 即使表单已提交,也会输出表单 HTML。

同样,如果表单尚未提交,则脚本应仅输出表单 HTML。 这就是阻止脚本正确重定向浏览器的原因。

脚本发送回浏览器的 HTTP 响应分为两部分:标头和正文。 标头必须首先发送,这意味着一旦您的脚本输出某些内容,PHP 就会将它当时拥有的任何标头发送到浏览器,以便它可以发送正文。

由于脚本在设置 location 标头之前尝试输出表单 HTML,因此无法执行重定向(当 PHP 在代码中计算 header() 语句时,HTTP 标头已经发送到浏览器)。

要解决此问题,您需要将表单处理代码移动到脚本的顶部:

<?php
  /* Check if the form was submitted. */
  if( $_POST ) {
    /* @todo Validate incoming values. */
    /* If all values are valid, insert into the database. */
    if ($stmt = $mysqli->prepare('INSERT INTO `forum_traede` (`kategori_id`, `bruger_id`, `tekst`, `dato`, `titel`) VALUES (?, ?, ?, NOW(), ?)')) {
      ...
      $stmt->close();
      $return_page = (
        isset($_POST['return_page'])
          ? $_POST['return_page']
          : 'http://xxxx.dk/bruger-forum-opret/'
      );
      header(sprintf('Location: %s', $return_page));
      exit;
    } else {
      ...
    }
  }
?>
<!-- If we get to this point, either the form has not been submitted yet,
      or there is an error, so we should display the form HTML.
  -->
<form action="#" method="post">
  ...
</form>

另请注意,我在header()调用后添加了exit;。 由于浏览器被重定向到新的URL,因此显示表单不再有意义。

另外,我对$return_page变量的目的有点不清楚。 我认为目的是在表单中指定表单处理后浏览器将被重定向到的 URL。 我更新了答案中的代码以反映这一点。