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

时间:2012-02-03 22:42:53

标签: c# asp.net postback

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

在使用下拉菜单后使用浏览器的“后退”按钮时,我注意到了一些奇怪的行为。程序是这样的:

  1. 在其中一个下拉列表中进行选择,从而导致回发和重定向。好到目前为止。
  2. 点击浏览器的“后退”按钮。
  3. 在之前使用的下方的另一个导航下拉列表中(它们全部包含在div中),进行选择。该页面会重定向到与第一次相同的URL,而不是基于此其他下拉列表重定向到的内容。
  4. 我已经在Firefox 10和IE9中尝试了这个并且看到了同样的事情。我查看了Firebug中的Net选项卡,并在第3步的POST中看到了正确的控件被引用。但是当我调试它时,错误下拉列表的事件处理程序(步骤1中使用的事件处理程序)将触发。

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

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

    下拉列表实际上不是普通的<asp:DropDownList ... />元素;我使用类似于this的方法插入optgroup个元素。

    C#示例:

    ddlTest.Click += new EventHandler(ddlTest_SelectedIndexChanged);
    

    ddlTest_SelectedIndexChanged

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

    这里发生了什么?

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

4 个答案:

答案 0 :(得分:3)

单击后退按钮后,显示上次访问的页面,因为它保存在客户端上不会再次发送回/从服务器请求,这包括最后发布的值,如您选择后显示的一次在点击后退按钮后立即获得第二个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

答案 1 :(得分:3)

Firefox - 添加代码:

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

Chrome - 添加javascript pageload:

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

答案 2 :(得分:2)

您可以使用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每次都会请求一个新版本的响应。

答案 3 :(得分:0)

@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);

    ....
}