【问题标题】:How can I 404 a page when headers is already sent out?当标头已经发出时,如何对页面进行 404 处理?
【发布时间】:2012-05-04 11:14:52
【问题描述】:

我使用 php GET 来检查 URL 中的 ID,然后从与该 ID 匹配的数据库中检索 tekst。这是执行此操作的代码的 sn-p:

function getLyric() {
$id = (int)$_GET['id'];
$query = mysql_query("SELECT * FROM lyrics WHERE id = ".$id."") or die(mysql_error());
if ($row = mysql_fetch_assoc($query)) { ?>
    <h1><?= $row['title']; ?> lyrics</h1>
    <h2><?= $row['author']; ?></h2>

    <pre><?= $row['lyrics']; ?></pre>
    <?php }
else { header("HTTP/1.1 404 Not Found"); header("Status 404 Not Found"); }
}

这就是我调用此函数时页面的外观:

<?php include 'includes/header.php' ?>
<?php getLyric(); ?>
<?php include 'includes/footer.php' ?>

很遗憾,函数中的 404 部分不起作用。我收到以下警告。Warning: Cannot modify header information - headers already sent by [redacted] functions.php on line 61。第61行是404部分; “其他{标题}〜”。

我从 Stackoverflow 了解到这是因为 &lt;html&gt;&lt;head&gt; 部分已经在页面上输出,所以我不能再更改它了。这是对的吗?

以前当我没有else~部分时,我只是得到一个空白页。在这种情况下如何正确发送 404?如果我不能,最接近的选择是什么?

【问题讨论】:

  • 仅供参考,您的代码对 SQL 注入完全开放,如果您还没有被黑客入侵,那么您将被黑客入侵。学习使用 PDO 进行准备好的查询,以完全避免这个问题。
  • 将字符串类型转换为 int 只会返回 0。除非从他的数据库中请求一个 id 0 神奇地打开了它的入侵,我不认为这有那么大的威胁。现在,如果他习惯以同样的方式直接使用字符串,我可以看出问题出在哪里。
  • 谢谢,我会调查的。 PDO 代表什么? @MetalFrog:我的字符串首先通过mysql_real_escape_string。我已经读过那会做的。
  • @Brad:不,输入已经过类型转换。
  • 够了。 PDO 是 PHP Data Objects 的缩写,它是一个与数据库交互的通用接口类。它不像 Brad 所说的那样是灵丹妙药,但如果您使用多个 DBM,它肯定会让事情变得更容易。 us.php.net/PDO

标签: php mysql http-headers http-status-code-404


【解决方案1】:

一旦您发送了 HTML 代码,您就无法更改标题。标头告诉浏览器如何处理请求的页面。如果它已经收到 HTML,它会假定 content-type:text\html 并开始渲染页面。此时您的标题已定义,您无法返回。

【讨论】:

    【解决方案2】:

    不,你不能,标题是第一个出去的东西,所以一旦你发回文本,标题就已经出了门。

    您可能希望在包含标题之前运行查询,运行检查以确保返回数据,然后打印。

    function getLyric() {
      $id = (int)$_GET['id'];
      $query = mysql_query("SELECT * FROM lyrics WHERE id = ".$id."") or die(mysql_error());
      if ($row = mysql_fetch_assoc($query)) {
        include 'includes/header.php'; 
      ?>
          <h1><?= $row['title']; ?> lyrics</h1>
          <h2><?= $row['author']; ?></h2>
    
          <pre><?= $row['lyrics']; ?></pre>
          <?php }
      else { header("HTTP/1.1 404 Not Found"); }
    }
    
    <?php getLyric(); ?>
    <?php include 'includes/footer.php' ?>
    

    【讨论】:

    • 感谢您的回复,这是我已经担心的。那么处理这个问题的最佳方法是什么?也许是 301 重定向?或者我应该完全重新设计我的结构(例如 html 标头上方的所有 PHP 内容)
    • 我在上面添加了一些示例代码,说明如何做到这一点。一旦你已经检查了你的结果,你就需要包含标题。根据实现方式的不同,它可能与其他页面有所不同。另一种方法是使用一个函数来检查歌词是否在标题之前存在,如果不存在则只返回 404。
    【解决方案3】:

    标头一旦发送,您将无法再发送。您可以做的是启用Output Buffering,它基本上会等到脚本执行完毕后再输出内容。这将允许您在脚本中的任何位置设置标题,但根据您的脚本会产生一些轻微的后果,您应该在盲目启用它之前阅读它。

    另外,阅读 SQL 注入。您的网站极易被利用。

    【讨论】:

      【解决方案4】:

      您可以在脚本中启用输出缓冲。

      例子:

      <?php
      ob_start();
      echo 'Hi';
      header('Location: http://someurl');
      exit;
      ?>
      

      请在使用前确定输出缓冲是什么。

      【讨论】:

        【解决方案5】:

        您只需要将逻辑与演示分开:

        function getLyric() {
            $id = (int)$_GET['id'];
            $query = mysql_query("SELECT * FROM lyrics WHERE id = ".$id."") 
                         or die(mysql_error());
            return mysql_fetch_assoc($query);
        }
        

        ...然后:

        <?php
        $row = getLyric();
        
        if (!$row) {
            header("HTTP/1.1 404 Not Found");
            exit;
        }
        
        include 'includes/header.php' 
        ?>
        
        <h1><?= $row['title']; ?> lyrics</h1>
        <h2><?= $row['author']; ?></h2>
        
        <pre><?= $row['lyrics']; ?></pre>
        
        <?php 
        include 'includes/footer.php'
        ?>
        

        【讨论】:

        • 谢谢,这是一个很好的选择。我不喜欢我必须在我的&lt;html&gt;&lt;head&gt; 上方有一段代码;)。哦,好吧。
        • @user1333327:将逻辑与表示分离被认为是最佳实践,并且在大型系统中是不可避免的。上面是一个简短的示例,但您通常希望将逻辑和表示存储在两个单独的文件中,并使用包含,因为它使它们可重用,例如:lyric.php 用于逻辑,并且lyric.phtml 用于演示。
        【解决方案6】:
        ob_start();
        header("location:somepage.php");
        ob_end_flush();
        

        实际上,如果你打开了输出缓冲,在你刷新它之前不会发送任何东西。甚至没有标题。

        【讨论】:

        • 这并不完全正确。输出缓冲区有一个限制(通常默认为 4096 字节),一旦达到它,它就会被刷新。此外,这只是一种解决方法,并不能真正解决问题。
        【解决方案7】:

        如您所见,当内容已发送时,无法发送 404。你有两个选择,IMO

        • 修改您的代码以启用输出缓冲。这实质上意味着您回显的所有内容或 PHP 标记之外的内容都会进入缓冲区并且不会发送到客户端。阅读它here

        • 修改函数以给出数据而不是 UI。这意味着函数 getLyrics() 应该给出相关行。在您include 任何标题之前,请务必检查该函数是否显示该记录存在。否则,您可以随时发送 404。这实际上是推荐的,并且是一个很好的设计,因为它本质上将逻辑与 UI 分离。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-09-17
          • 1970-01-01
          • 2015-06-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-24
          相关资源
          最近更新 更多