后重定向(PRG)在Webkit浏览器中重新插入相同页面到历史记录中。

11

假设我在 /page?id=1 上。

然后我导航到 /page?id=2。

我对那个页面进行了更改,这实现了一个post并重定向回 /page?id=2。

在Firefox中,我可以点击一次返回按钮返回到 /page?id=1,但是在iPhone上的Chrome和Safari中,我需要点击两次返回按钮,因为/page?id=2在浏览器历史记录中出现了两次。(如果我从id=2进行了多次发帖,我就必须点击相应次数的返回按钮才能最终返回id=1。)

从某些方面来说,这似乎是正常的浏览器行为,因为每个GET都被简单地推入历史记录中,但由于URL与先前条目相同,这导致了糟糕的用户体验,其他Web应用程序通常会避免这种情况...而且在Firefox中自然也会避免。

这是Webkit浏览器中不可避免的错误,还是我可以以不同的方式实现PRG来避免这种情况?

顺便提一下- 行为似乎在使用302或303重定向时相同。

更新: 我模拟了一些示例代码...不知道是否有像jsfiddle这样的平台,可以将其上传供您查看:

form.php:

id=<?=$_REQUEST['id']?>
<form action="submit.php" method="post">
<input type="hidden" name="id" value="<?=$_REQUEST['id']?>">
<input type="submit" value="submit">
</form>

提交.php:

<?php
header("Location: form.php?id=" . $_REQUEST['id']);
die($_REQUEST['id']);
?>
如果我从form.php?id=4(只是为了将其放入浏览器历史记录)开始,然后转到form.php?id=5,然后点击提交(就像执行数据库更改一样),在Firefox中,我会为每个页面得到一个历史记录条目;而在Chrome中,我会得到id=4的一个条目,然后是id=5的两个条目。为什么行为不同?我认为Firefox的行为更好,因为连续点击两次返回键离开id=5对用户来说是反直觉的。
2个回答

1
我们也遇到了同样的问题,即使研究了好几天,我也没有找到“简单”的解决方案。最接近的是这个Webkit Bugzilla ticket,看起来它似乎不是非常重要。正如您所提到的,IE和Firefox表现得很好。
由于我们在应用程序中有自己的返回按钮,我们能够通过使用会话存储并在页面加载时进行检查来解决该问题。TypeScript代码如下:
class DoubleHistoryWorkaround {

    // key of the attribute we store the URL we where on when clicking on the back button
    private static comingFromLabel = "comingFromURL";
    // key of the attribute of the flag denoting whether we set a valid comingFromURL
    private static comingFromFlag = "comingFromSet";

    constructor() {
        this.checkLocation();
    }

    /**
     * Checks the URL we saved in the session and goes a further step back
     * in the history if the first back button brought us to the same page again.
     */
    private checkLocation() {
        let doubleEntry : boolean;
        // have we set a comingFromURL?
        let comingFromSet = window.sessionStorage.getItem(DoubleHistoryWorkaround.comingFromFlag);
        if (comingFromSet) {

            // is the set comingFromURL the same as our current page?
            let currentURL = window.location.href;
            let comingFromURL = window.sessionStorage.getItem(DoubleHistoryWorkaround.comingFromLabel);
            if (currentURL === comingFromURL) {
                // double history entry detected
                doubleEntry = true;

                // before we skip we save our location ourselves, since we might still navigate
                // to the same page again (in case of triple identical history entries)
                DoubleHistoryWorkaround.saveLocation();

                // skip this page
                history.back();
            }
        }

        // reset the location entry unless we just set it ourselves
        if (!doubleEntry) {
            this.resetLocation();
        }
    }

    /**
     * Saves the current location in the session storage.
     */
    public static saveLocation() {
        window.sessionStorage.setItem(DoubleHistoryWorkaround.comingFromFlag, "true");
        window.sessionStorage.setItem(DoubleHistoryWorkaround.comingFromLabel, window.location.href);
    }

    /**
     * Removes the set location from the session storage.
     */
    private resetLocation() {
        window.sessionStorage.setItem(DoubleHistoryWorkaround.comingFromFlag, "false");
        window.sessionStorage.setItem(DoubleHistoryWorkaround.comingFromLabel, "");
    }
}

当我们点击应用程序的返回按钮时,调用 DoubleHistoryWorkaround.saveLocation(),设置由 checkLocation() 检查的会话条目。


1
虽然这不是一个关于发生了什么的解释,但我有一个方法可以在我的所有应用程序中使用。首先是一些代码: < p > form.php 的代码如下:

id=<?=$_REQUEST['id']?>
<form action="submit.php" target="iframe" method="post">
    <input type="hidden" name="id" value="<?=$_REQUEST['id']?>">
    <input type="submit" value="submit">
</form>

"

submit.php像这样:

"
<?php
    header("Location: form.php?id=" . $_REQUEST['id']);
    die($_REQUEST['id']);
?
<script>
    window.parent.location.reload();
</script>

“这是您的文档,其中包含一些额外的内容在<form>标签和一个全新的<script>标签中。”
“此外,文档中还可能包含一个iframe,如下所示:”
<iframe name="iframe"></iframe>

所以,为了解释清楚。不是每次需要进行更改时导航到新网站并返回,而是在现有文档中使用iframe加载submit.php。因此出现了target="iframe"部分。
然后当iframe加载完成,也就是更改已经完成时,你重新加载原始页面以反映这些更改,因此出现了window.parent.location.reload();部分。由于页面只是重新加载,因此不会在历史记录中添加第二个条目。
希望这可以帮助你 :)

谢谢。我明白它的工作原理,但哇,这是一个相当绕弯子的方法,需要使用iframe、javascript等来实现一个简单的表单提交(并希望浏览器不会因为iframe而污染其历史记录)。我仍然希望有人能够解决Webkit中标准PRG期间浏览器历史记录行为的基本机制,并看看是否有更直接的修复方法来解决它。 - dlo
谢谢您的好评。是的,获得关于机制如何实际工作的真正解释会很不错。 - atjn
是的,我曾经很幸运地问了一个关于Firefox的问题,然后一个真正在Firefox开发团队中的人回答了我。 :) - dlo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接