使用浏览器“后退”按钮后,ASP.NET从错误的下拉列表回发

本文关键字:错误 NET 下拉列表 ASP 浏览器 后退 按钮 | 更新日期: 2023-09-27 18:22:21

我有一个ASP.NET 2.0 web应用程序,它部分通过使用DropDownList控件进行导航。当其中一个更改时,会导致回发,然后根据下拉列表中选择的内容将其重定向到另一个URL。

我注意到在使用下拉菜单后使用浏览器的"后退"按钮时出现了奇怪的行为。程序如下:

  1. 在其中一个下拉列表中进行选择,导致回发和重定向。到目前为止还不错
  2. 单击浏览器的"后退"按钮
  3. 在之前使用的导航下拉列表下方的另一个导航下拉列表中(它们都包含在一个div中),进行选择。页面重定向到与第一次相同的URL,而不是根据其他下拉列表重定向到的URL

我在Firefox 10和IE9中都尝试过,看到了同样的东西。我查看了Firebug中的Net选项卡,发现在步骤3的POST中,引用了正确的控件。但当我调试它时,错误下拉列表(步骤1中使用的下拉列表)的事件处理程序会启动。

代码非常简单明了。标记示例:

<asp:DropDownList runat="server" ID="ddlTest" AutoPostBack="true" />

下拉列表实际上并不是简单的<asp:DropDownList ... />元素;我使用类似的方法插入optgroup元素。

C#示例:

ddlTest.Click += new EventHandler(ddlTest_SelectedIndexChanged);

并且在ddlTest_SelectedIndexChanged:中

if (ddlTest.SelectedValue != "")
{
   Response.Redirect(MyUtilClass.GetUrl(ddlTest.SelectedValue));
}

这是怎么回事?

2012年6月2日更新:我通过在SelectedIndexChanged事件处理程序中检查Request["__EVENTTARGET"]的内容来修复此问题。不过,我仍然很好奇为什么会发生这种事。为什么第一个事件重复?为什么只有当第二次回发发生在第一次回发之下的控件时才会发生?

使用浏览器“后退”按钮后,ASP.NET从错误的下拉列表回发

单击后退按钮后,会显示上次访问的页面,因为它保存在客户端上,不会再次向服务器发送/请求,这包括上次发布的值,如您在单击后退按钮之后选择第二个DropDownlist后显示的值。

IE上的onload方法(有关其他浏览器,请参阅:http://dean.edwards.name/weblog/2005/09/busted/)当你按下第二个页面上的后退按钮时,第一个页面的会被触发,所以使用它,我们可以在重定向页面之前尝试在客户端上保存回发操作,我们用JS将其保存在一个隐藏值上,然后,一旦用户返回,onload方法将评估隐藏的是否有值,如果有,则将页面的位置更改为自身,以便再次请求页面,并清除所有发布的值:

page1.aspx

 <script type="text/javascript">
        function redir(){    
            var h=document.getElementById('hdR');
            if (h.value=='1'){
                h.value='0';
                document.location.href='page1.aspx';
            }         
        }
        function changeHids(){
            var h=document.getElementById('hdR');
            h.value='1';
        }
        </script>
    </head>
    <body onload="redir();">
        <form id="form1" runat="server">
        <div>
            <asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="true" onchange="changeHids();">
                <asp:ListItem>1</asp:ListItem>
                <asp:ListItem>2</asp:ListItem>
                <asp:ListItem>3</asp:ListItem>
                <asp:ListItem>4</asp:ListItem>
            </asp:DropDownList>
            <br />
            <asp:DropDownList ID="DropDownList2" runat="server" AutoPostBack="true" onchange="changeHids();">
                <asp:ListItem>5</asp:ListItem>
                <asp:ListItem>6</asp:ListItem>
                <asp:ListItem>7</asp:ListItem>
                <asp:ListItem>8</asp:ListItem>
            </asp:DropDownList>
            <asp:HiddenField ID="hdR" runat="server"/>
        </div>
        </form>
    </body>
Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged
    Response.Redirect("page2.aspx?ddl=1&val=" & Me.DropDownList1.SelectedValue, True)
End Sub
Protected Sub DropDownList2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList2.SelectedIndexChanged
    Response.Redirect("page2.aspx?ddl=2&val=" & Me.DropDownList2.SelectedValue, True)
End Sub

page2.aspx

  Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Response.Write("ddl:" & Request.QueryString("ddl") & " " & "value:" & Request.QueryString("val"))
    End Sub

Firefox-添加代码隐藏:

protected void Page_Load(object sender, EventArgs e)
{ 
    Response.AppendHeader("Cache-Control", "no-store");
}

Chrome-添加javascript页面加载:

var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
if (is_chrome) {
    document.getElementById("form1").reset();
}

您可以使用Response.Cache属性来解决此问题,因为您允许在浏览器内存中缓存列表。来自MSDN SetAllowResponseInBrowserHistory

当HttpCacheability设置为NoCache或ServerAndNoCache时Expires HTTP标头默认设置为-1;这个告诉客户不要在"历史记录"文件夹中缓存响应,以便在使用后退/前进按钮客户端请求新版本的响应每次。您可以通过调用带有allow参数集的SetAllowResponseInBrowserHistory方法真实。

在页面加载方法中,您可以添加此行。

 protected void Page_Load(object sender, EventArgs e)
  {
       Response.Cache.SetCacheability(HttpCacheability.NoCache); 
       Response.Cache.SetAllowResponseInBrowserHistory(false);
        // or else you can do like this 
       Response.ExpiresAbsolute = DateTime.Now.AddDays(-1d);
        Response.Expires = -1;
        Response.CacheControl = "No-cache";
  }

因此,每当您按下返回键时,t每次都会请求一个新版本的响应。

@Ravi的回答略有变化,这对我很有用…

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);
    Context.Response.AddHeader("Cache-Control", "max-age=0,no-cache,no-store");
    Context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    Context.Response.Cache.SetMaxAge(TimeSpan.Zero);
    ....
}