更多重定向后获取问题,我做错了什么

时间:2013-06-22 03:43:17

标签: php forms google-chrome post-redirect-get

我正在围绕一个更新的范例重写一堆表单,尝试简化用于防止在用户使用“后退”按钮进行播放时过时处理过时表单数据的机制。我把它简化为一个最小的测试用例,它在FF21中工作,ie10(!),Opera12.15,但只有那么用Chrome 27.0和Safari 5.1.7 - 不是我预期的问题的浏览器,所以这可能是我自己的错。

表单view.php始终发布到post.php,后者执行所有工作,然后重定向到view.php。 (其中一些是设计页面,其中一些用户迭代多次。)我的目标是避免“浏览器必须重新提交数据”消息,以防止浏览器使用缓存视图,并保持view.php不会长时间离开历史上的踪迹。 (有些消息来源称PRG阻止了这一点。)在FF,Opera和IE中都没有达到这三个目标,而且没有历史记录。 Chrome和Safari会在历史记录中显示相同的网页浏览量。查看请求/响应标头,Chrome似乎会重新访问任何历史页面的完整GET,但它会在同一页面上显示历史和当前字段值的混合(!)。对于我来说,这是最令人困惑的一个方面,我认为,显示一个页面上永远不会共存的价值组合。 Chrome中的查看来源始终会显示最新的副本。我正在谈论下面的确切蒸馏实例的行为,而不是它们的一些更复杂的版本。

字段1回显内部计数器并忽略用户输入。无论是否为只读,它的行为方式都是相同的。

字段2回应用户输入。我通常会复制或追加第一个字段。

view.php

<?php

session_start();
header('Cache-Control:private,no-store,no-cache,must-revalidate,post-check=0,pre-check=0');
header('Pragma: no-cache');


$cval = !isset($_SESSION['counter']) ?  0 : $_SESSION['counter'];
$word = !isset($_SESSION['word'])    ? '' : htmlentities($_SESSION['word'],ENT_QUOTES,'UTF-8',FALSE);

echo <<<HTM
<pre>
<form method='POST' action='post.php'>
<input type='text' name='count' value='$cval'/>
<input type='text' name='word' value='$word'/>
<input type='submit' name='doit' value='Submit'/>
</form>
</pre>
HTM;

?>

post.php中

<?php

session_start();

if(!isset($_SESSION['counter'])):
    $_SESSION['counter'] = 1;
else:
    $_SESSION['counter'] += 1;
endif;

$_SESSION['word'] = !isset($_POST['word']) ? '' : $_POST['word'];

header('Location: view.php');


?>

如果我开始在历史记录中进行备份,Chrome会显示最新的Field-one值,但会显示历史字段二值。偶尔它会显示两者的历史。如果没有这种差异,我认为无法阻止服务器(在配对网络上)或代理缓存页面。

如果我添加'Expires:-1'标题,它会做同样的事情。

我可以处理识别过时的形式;我似乎无法阻止用户访问或提交一个。 view.php上的递增GET参数将阻止不可预测的缓存视图,但会在所有浏览器中创建显式历史记录,并且只邀请人们向后转动棘轮。

为什么会发生这种情况并且可以修复?

1 个答案:

答案 0 :(得分:0)

在没有相反建议的情况下,我的p-r-g模型的实现似乎没有什么不妥。 Firefox,IE和Opera完全按照预期工作,Safari只是盲目地将每个GET纳入历史。但是,通过在表单中​​插入隐藏或只读新鲜令牌可以轻松处理Safari的历史记录,以便您可以识别陈旧形式。

绊脚石是一个Chrome怪癖,我看不到纯粹的PHP / HTML方式。 (Duly报道,BTW。)

这是新代码,只有smidgen更复杂,这使得行为非常清晰。只需做几个提交,每次在字段Inval中键入不同的内容,然后回顾历史。

即使您返回历史记录,Chrome也始终正确提取视图的新副本。但是,如果您再次访问#x,Chrome会覆盖您访问#x时触及的任何字段的预填充值,放入陈旧数据而不是指定的新视图。 (检查字段Inval中的数据与右侧显示的预填充值之间的不匹配。)通过使用JavaScript通过将预填充数据从嵌入页面中的隐藏元素复制到其中来重新预填充损坏的字段,这是可以解决的。

新的VIEW.PHP

<?php # view.php

session_start();
header('Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Expires: -1');

# Format timestamp and counter
$tval = date("H:i:s");
$cval = !isset($_SESSION['counter']) ?  0 : $_SESSION['counter'];

# Format last-posted values for display but (mostly) not for prepopulation
$lphiddn = !isset($_SESSION['xhiddn'])    ? '' : htmlentities($_SESSION['xhiddn'],ENT_QUOTES,'UTF-8',FALSE);
$lpcount = !isset($_SESSION['xcount'])    ? '' : htmlentities($_SESSION['xcount'],ENT_QUOTES,'UTF-8',FALSE);
$lpinval = !isset($_SESSION['xinval'])    ? '' : htmlentities($_SESSION['xinval'],ENT_QUOTES,'UTF-8',FALSE);

# Format pre-population data
$phiddn = "H $tval";          //  H 00:00:00
$pcount = "(#$cval) $tval";   //  (#1) 00:00:00
$pinval = $lpinval;           //  whatever was last posted for this field

echo <<<HTM
<pre style='font-family: monospace;'>
<form method='POST' action='post.php'>
Hiddn <input type='hidden' name='hiddn' value='$phiddn'/> prepop=$phiddn   lastpost=$lphiddn
Count <input type='text'   name='count' value='$pcount'/> prepop=$pcount     lastpost=$lpcount
Inval <input type='text'   name='inval' value='$pinval'/> prepop=$pinval = lastpost=$lpinval
Submt <input type='submit' name='doit' value='Submit'/>
</form>
</pre>
HTM;

?>

新的POST.PHP

<?php # post.php

session_start();
header('Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Expires: -1');

if(!isset($_SESSION['counter'])):
    $_SESSION['counter'] = 1;
else:
    $_SESSION['counter'] += 1;
endif;

$_SESSION['xhiddn'] = !isset($_POST['hiddn']) ? '' : $_POST['hiddn'];
$_SESSION['xcount'] = !isset($_POST['count']) ? '' : $_POST['count'];
$_SESSION['xinval'] = !isset($_POST['inval']) ? '' : $_POST['inval'];
header("HTTP/1.1 303 See Other");
header('Location: view.php');


?>