|
蓝森林 http://www.lslnet.com 2006年8月25日 8:28
[asp.net]页面刷新的防范机制
Detecting Page Refresh”(我的dote blog里)里介绍了一种判断用户刷新的办法,不错,可以其中的原理确着实让我头痛了好一会,不过现在终于想明白了,写到这里,大家看看看是不是这么回事
里面说用重写loadviewstate和saveviewstate两个方法来实现变量值的改变,如果大家把代码写到vs里调试一下就会发现,不论是refresh还是postback,程序的流程都是一样,即:先走一遍loadviewstate,然后走两遍saveviewstate,既然是一样的,那为什么得出得结果却不相同?原因就在于:当你postback的时候,saveviewstate中的数据就被提交到服务器,可是当你refresh的时候,saveviewstate的数据并没有被提交,这样导致的结果就是allStates[1]的值的不同,在postback的时候,loadviewstate中使用的是这次返回的allStates[1],可是refresh的时候,loadviewstate中使用的是上次返回的allStates[1]。
_refreshState和_isRefresh在每次的初始值都是false,下面是两个流程的分析:
postback前发生的SaveViewState
protected override object SaveViewState()
{
//Session["__ISREFRESH"]的值为false
Session["__ISREFRESH"] = _refreshState;
object[] allStates = new object[2];
allStates[0] = base.SaveViewState();
//allStates[1]的值为true
allStates[1] = !_refreshState;
return allStates;
}
用户postback
先引发LoadViewState
protected override void LoadViewState(object savedState)
{
object[] allStates = (object[]) savedState;
base.LoadViewState(allStates[0]);
//allStates[1]的值为true,所以_refrehState也为true
_refrehState = (bool) allStates[1];
//true和false相比,所以_isRefresh为false
_isRefresh = _refreshState == (bool) Session["__ISREFRESH"];
}
紧接着进入SaveViewState
//postback后发生的SaveViewState
protected override object SaveViewState()
{
//_refreshState在loadviewstate中被改为true,所以Session["__ISREFRESH"]的值为true
Session["__ISREFRESH"] = _refreshState;
object[] allStates = new object[2];
allStates[0] = base.SaveViewState();
//allStates[1]的值为false
allStates[1] = !_refreshState;
return allStates;
}
然后用户refresh,进入LoadViewState
protected override void LoadViewState(object savedState)
{
object[] allStates = (object[]) savedState;
base.LoadViewState(allStates[0]);
//allStates[1]使用前一次的值,即true
_refrehState = (bool) allStates[1];
//这里Session["__ISREFRESH"]也是true,所以_isRefresh的值变成true
_isRefresh = _refreshState == (bool) Session["__ISREFRESH"];
}
这样说是有点糊涂,汗……不知该怎样表达,但是这个过程如果放到vs里进行一下单步调试,则配合上面的过程,应该就很容易看出了。
最后说一下实际应用,当然是先创建一个继承自System.Web.UI.Page的PageBase,然后在PageBase内重写那两个方法,最后让需要使用该功能的页面继承PageBase。
|
Detecting Page Refresh
Steven Bey
12.11.2004
Introduction
A common problem that Web Application Developers encounter is how to stop the user from refreshing the page. This is a problem if the previous request to the server was a PostBack, which, for example, inserted the WebForm’s data into a database. The result: duplicate rows in the database. The answer to this problem is that you can’t stop the user from refreshing the page, however there is a way to determine if this event has occurred.
In his article “Build Your ASP.NET Pages on a Richer Bedrock” Dino Esposito outlined a mechanism to detect a page refresh. This method is cumbersome and more complicated than necessary, although the fundamental idea is sound and forms the basis of this solution. Dino’s mechanism uses a counter stored on the page and a session variable to store the previous request’s counter on the server, if the two match then we have a page refresh.
A Simpler Method
Keeping the whole process within a base Page Class ensures that the mechanism is completely encapsulated (and simple to implement) and if we use ViewState we eliminate the need to use an additional hidden field. Also, as we simply want to test if the two storage devices contain the same value, we can use two boolean variables, which further simplifies the process.
The last decision to make is where, in the Page’s lifecycle, should the process take place. As we are using ViewState it would seem logical to perform the operation in the LoadViewState and SaveViewState methods. Using these two methods, and not the OnLoad method, has further benefits in that it eliminates potential problems with sub-classes implementing Page_Load.
How The Process Works
The LoadViewState method, which is part of the Page’s initialisation phase, is only invoked during PostBack and therefore SaveViewState is the only method, of the two ViewState related methods, to be called when the page is first requested.
protected override object SaveViewState()
{
Session["__ISREFRESH"] = _refreshState;
object[] allStates = new object[2];
allStates[0] = base.SaveViewState();
allStates[1] = !_refreshState;
return allStates;
}
Note: _refreshState (which on initial page request is defaulted to false and on subsequent PostBack requests is the value of ViewState) is assigned to the Session["__ISREFRESH"] item and the negated _refreshState is saved to the new ViewState.
Once a PostBack event takes place the LoadViewState method is called.
protected override void LoadViewState(object savedState)
{
object[] allStates = (object[]) savedState;
base.LoadViewState(allStates[0]);
_refreshState = (bool) allStates[1];
_isRefresh = _refreshState == (bool) Session["__ISREFRESH"];
}
Note: The _refreshState is retrieved from ViewState and compared with the value in the Session["__ISREFRESH"] item. The result is stored in _isRefresh, which is used by the IsRefresh Property.
The listing below shows the entire class definition:
namespace StevenBey.Web.UI
{
public class Page : System.Web.UI.Page
{
private bool _refreshState;
private bool _isRefresh;
public bool IsRefresh
{
get
{
return _isRefresh;
}
}
protected override void LoadViewState(object savedState)
{
object[] allStates = (object[]) savedState;
base.LoadViewState(allStates[0]);
_refrehState = (bool) allStates[1];
_isRefresh = _refreshState == (bool) Session["__ISREFRESH"];
}
protected override object SaveViewState()
{
Session["__ISREFRESH"] = _refreshState;
object[] allStates = new object[2];
allStates[0] = base.SaveViewState();
allStates[1] = !_refreshState;
return allStates;
}
}
}
Testing The Process
<%@ Page Inherits="StevenBey.Web.UI.Page" %>
<html>
<head>
<title>Detecting Page Refresh</title>
</head>
<body>
<form runat="server">
<asp:button Text="Test Refresh" runat="server" />
</form>
IsRefresh = <%= IsRefresh %>
</body>
</html>
Clicking the “Test Refresh” button invokes a PostBack, however the value of IsRefresh doesn’t change until you click on the browser’s Refresh button or press F5 on the keyboard (and then click “Retry”). Clicking the “Test Refresh” button once again resets the value of IsRefresh to false.
Live Demo
|
|