4

我正在围绕一个较新的范例重写一堆表单,试图简化用于防止在用户使用“后退”按钮时重新处理过时表单数据的机制。我已经将它提炼成一个在 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 中查看源代码始终显示最新的副本。我说的是下面精确提炼示例的行为,而不是它们的一些更复杂的版本。

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

字段二回显用户输入。我通常将字段一复制或附加到它。

视图.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 倾向于显示最新的字段一值,但显示历史字段二值。有时它在两者上都显示历史。如果没有这种差异,我认为阻止服务器(在配对网络上)或代理缓存页面是失败的。

如果我添加一个“过期:-1”标题,它会做同样的事情。

我可以处理识别陈旧的表格;我似乎不能做的是阻止用户到达或提交一个。view.php 上递增的 GET 参数将防止不可预知的缓存视图,但会在所有浏览器中创建明确的历史记录,并且只会邀请人们向后转动棘轮。

为什么会发生这种情况,是否可以修复?

4

1 回答 1

0

在没有相反建议的情况下,我对 prg 模型的实施似乎没有什么太大的问题。Firefox、IE 和 Opera 完全按照预期工作,而 Safari 只是盲目地将每一个 GET 放入历史记录中。但是 Safari 的历史很容易通过在表单中​​插入隐藏或只读的新鲜度标记来处理,这样您就可以识别过时的表单。

绊脚石是 Chrome 的怪癖,我看不到纯 PHP/HTML 的解决方法。(正式报告,顺便说一句。)

这是新代码,只是稍微复杂一点,它使行为非常清晰。只需提交几次,每次在 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');


?>
于 2013-06-23T03:47:23.653 回答