ASP.NET防止页面刷新的两种解决方法小结
方法有二,总结如下:
第一方法:
直接在cs代码里敲:
response.buffer = true;
response.expiresabsolute = datetime.now.addseconds(-1);
response.expires = 0;
response.cachecontrol = "no-cache";
当有人想按后退时页面已过期,效果就达到了
第二方法:
submitoncepage:解决刷新页面造成的数据重复提交问题(网上资料)
执行过postback操作的web页面在刷新的时候,浏览器会有“不重新发送信息,则无法刷新网页”的提示,若刚刚执行的恰好是往数据库插入一条新记录的操作,点[重试]的结果是插入了两条重复的记录,以前一直是用保存数据后重新转向当前页面的方法解决,最近又找到了一个新的方法。
问题分析
在system.web.ui.page类中,有一个名为viewstate属性用以保存页面的当前视图状态,观察每个aspx页面最终生成的html代码可以发现,其实就是向页面添加了一个名为__viewstate的隐藏域,其value值就是页面的当前状态,每次执行postback过后,该 value值都会发生变化,而刷新页面则不会改变。
针对这种情况,我们可以在页面代码执行的末尾将当前的viewstate写到一个session中,而在页面加载时则判断该session值是否与当前 viewstate相等(其实session值恰好是viewstate的前一状态),若不等,则是正常的postback,若是相等则是浏览器刷新,这样一来,只要在我们的数据插入代码外嵌套一个if判断就可以达到防止数据重复提交的目的了。
其实到这里问题还没有完全解决,具体说来就是session的键值问题。假设我们将viewstate保存为 this.session["myviewstate"],如果一个用户同时打开两个防刷新提交的页面就乱套了,那针对页面的url设置session的键值呢?还是不行,因为用户有可能在两个窗口中打开同一页面,所以必须为每次打开的页面定义唯一的session键值,并且该键值可以随当前页面实例一起保存,参考viewstate的保存方式,我们直接向页面添加一个隐藏域专门存放session键值就可以了。
经oop80和edward.net的提醒,为了尽可能地降低session数据对服务器资源的占用量,现将上述方案略做调整,将viewstate利用md5加密后返回的32位字符串写入session。
另外,由于本方法会生成额外的session占用服务器资源,所以请在必须保留当前页面状态的情况下使用,若无需保留当前页面状态,则在完成数据提交后直接重定向到当前页面即可。
submitoncepage
submitoncepage是针对上述分析写的一个继承自system.web.ui.page的基类,需要防止刷新重复提交数据的页面从该基类继承,源码如下:
namespace mycontrol
{
/// <summary>
/// 名称:submitoncepage
/// 父类:system.web.ui.page
/// 描述:解决浏览器刷新造成的数据重复提交问题的page扩展类。
/// 示例:if (!this.isrefreshed)
///{
/////具体代码
///}
/// </summary>
public class submitoncepage:system.web.ui.page
{
private string _strsessionkey;
private string _hiddenfieldname;
private string _strlastviewstate;
public submitoncepage()
{
_hiddenfieldname = "__lastviewstate_sessionkey";
_strsessionkey = system.guid.newguid().tostring();
_strlastviewstate = string.empty;
}
public bool isrefreshed
{
get
{
string str1 = getsessincontent();
_strlastviewstate = str1;
string str2 = this.session[getsessinkey()] as string;
bool flag1 = (str1 != null) && (str2 != null) && (str1 == str2);
return flag1;
}
}
protected override void render(system.web.ui.htmltextwriter writer)
{
string str = getsessinkey();
this.session[str] = _strlastviewstate;
this.registerhiddenfield(_hiddenfieldname, str);
base.render(writer);
}
private string getsessinkey()
{
string str = this.request.form[_hiddenfieldname];
return (str == null) ? _strsessionkey : str;
}
private string getsessincontent() {
string str = this.request.form["__viewstate"];
if (str == null) {
return null;
}
return system.web.security.formsauthentication.hashpasswordforstoringinconfigfile(str, "md5");
}
}
}
测试项目
首先将submitoncepage类的源码编译成一个单独的dll,然后进行测试,步骤如下:
1、新建一个asp.net web应用程序;
2、添加submitoncepage类对应的dll引用;
3、给webform1添加一个label控件(label1)和一个button控件(button1);
4、设置label1的text为0;
5、双击button1转到codebehind视图;
6、修改类webform1的父类为submitoncepage并添加测试代码,结果如下:
public class webform1 : mycontrol.submitoncepage
{
protected system.web.ui.webcontrols.label label1;
protected system.web.ui.webcontrols.button button1;
#region web 窗体设计器生成的代码
override protected void oninit(eventargs e)
{
//
// codegen: 该调用是 asp.net web 窗体设计器所必需的。
//
initializecomponent();
base.oninit(e);
}
/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void initializecomponent()
{
this.button1.click += new system.eventhandler(this.button1_click);
}
#endregion
private void button1_click(object sender, system.eventargs e)
{
int i=int.parse(label1.text)+1;
label1.text = i.tostring();
if (!this.isrefreshed)
{
writefile("a.txt", i.tostring());
}
writefile("b.txt", i.tostring());
}
private void writefile(string strfilename,string strcontent)
{
string str = this.server.mappath(strfilename);
system.io.streamwriter sw = system.io.file.appendtext(str);
sw.writeline(strcontent);
sw.flush();
sw.close();
}
}
7、按f5运行,在浏览器窗口中连续点击几次button1,然后刷新几次页面,再点击几次button1;
8、转到测试项目对应目录下,打开a.txt和b.txt文件,可看到if (!this.isrefreshed) 的具体效果。