页面状态保持机制(编辑中)
Web应用程序中,有很多状态需要在页面的反复回调中能够保持住,还有一些状态需要在页面之间保持。对于状态的保持,是一个值得研究的问题。状态处理不当是页面失效或错误的一个重要的原因。
对于ASPX页面来说,控件可以通过VIEWSTATE来保持状态。VIEWSTATE机制非常好用,有时甚至可以用来保存页面后台代码中属性变量的状态值:因为变量的状态在回调时是不保存的,但是控件的状态却可以保持,因此可以通过控件来保持变量的状态,把控件设置为隐藏状态就不影响页面显示了。
但是VIEWSTATE却不能包打天下,我们的很多页面处理,都是以URL调用的方式进行的,如分页浏览,每次通过分页器进行的跳转都是新开页面,无法使用VIEWSTATE。
分页处理中,使用了URL参数来传递状态,这种传递方式简单明了,但也存在问题:
1、复杂。需要在URL中把各种状态全部写进去,一个都不能少。参数数量众多,考虑不周往往容易遗漏,还不好找原因。
2、和其他方式之间的协调问题。因为页面本身可能有回调操作,比如查询,或者其他的需要回调页面的控件操作,URL参数和回调参数之间的协调必须要精确的处理好。
特别是第二点,在分页浏览中体现得非常明显:既要能在不指定查询条件的情况下浏览所有数据,又要能够支持在回调事件中处理查询操作,还要能够把查询条件传递给新的分页器。要实现这一要求,只能借助复杂的处理逻辑来实现了。
这种需要在URL中传递所有参数的方式,在构造分页器链接的时候需要把页面所需的参数都显式地进行传递。当页面还有其他参数,特别是和分页无关的参数的时候,就会很难控制。构造分页器时,要去解析和分页无关的参数,要进行参数集合重复性的判断以及决定究竟哪个参数有效等。这些操作对于分页处理程序而言,既不合理也是隐患多多的。
基于页面的参数保持机制
参数传递的种种不便之处,使人不禁想到,为什么非要使用URL呢?URL方式,适合传递一些变化的参数。而上述的问题,都是由于一些需要保持的参数的传递而引起的。对于参数的保持,还有更加合适的手段:如Session或者Cookie。
那么,究竟选择Cookie还是Session呢?Session是一个进程级别的状态保存机制,在整个浏览过程中,在打开的所有页面之间,Session保存的数据都会有效。但Session也存在不足:
1、Session存放在服务器端,占用服务器的资源;
2、多个页面公用Session变量,容易导致混乱,如果每个页面都分别创建Session变量,则又造成资源的浪费;
3、Session本身有失效周期,在一些需要长期打开工作的页面,带来页面失效问题。
而Cookie相对来说,正好没有Session的不足。首先,Cookie不占用服务器资源,其次,Cookie按键-值的方式存储,正好可以用每个页面的名称为key,存储每个页面的状态。
根据各种应用的需要,基于页面的状态保持机制应该达到如下的要求:
1、页面回调时保持状态
2、页面跳转时保持状态
3、以Cookie方式存储数据
4、通过索引器的方式访问
5、兼容各种状态机制,自动尝试从URL、Session、Cookie中获取需要的参数值
6、只要使用过的参数,自动保持到Cookie中
页面状态保存机制的实现
/// <summary>
/// WebPage 的摘要说明
/// 用于网页的参数的缓存,可以将多种类型的参数缓存起来:以Url中的参数权限最高。URL中的参数会自动保存在Cookie(以网页名称命名)中,每次可以自动加载Cookie。
/// 非缓存参数,可以用Querystring来访问。
/// </summary>
public class WebPara
{
Page page; //所在的网页
private Dictionary<string, string> Paras = new Dictionary<string, string>();
string strPage;
/// <summary>
/// 构造函数
/// </summary>
public WebPara()
{
page = (Page)HttpContext.Current.Handler;
init();
}
/// <summary>
/// 构造
/// </summary>
/// <param name="WebPage"></param>
public WebPara(Page WebPage)
{
page = WebPage;
init();
}
void init() //初始化
{
strPage = System.Text.RegularExpressions.Regex.Match(page.Request.FilePath, "(?<=/)[^/]*Aspx", System.Text.RegularExpressions.RegexOptions.IgnoreCase).Value.ToLower(); //页面统一为小写,cookie区分大小写
//从cookie中加载所有的参数
HttpCookie ParaCookie = page.Request.Cookies[strPage]; //以页面命名的Cookie
if (ParaCookie != null)
{
for (int i = 0; i < ParaCookie.Values.AllKeys.Length; i++)
{
string parakey = ParaCookie.Values.AllKeys[i];
Paras.Add(parakey.ToLower(),HttpUtility.UrlDecode(ParaCookie[parakey]));
}
}
//从URL中覆盖加载参数
for (int i = 0; i < page.Request.QueryString.Count; i++)
{
if (Paras.ContainsKey(page.Request.QueryString.GetKey(i).ToLower())) //变量统一用小写
{
Paras[page.Request.QueryString.GetKey(i).ToLower()] = page.Request.QueryString[i];
}
else
Paras.Add(page.Request.QueryString.GetKey(i).ToLower(), page.Request.QueryString[i]);
}
//回写所有参数到Cookie中
foreach (KeyValuePair<string, string> kvp in Paras)
{
page.Response.Cookies[strPage][kvp.Key] = HttpUtility.UrlEncode(kvp.Value);
page.Response.Cookies[strPage].Expires = DateTime.Now.AddDays(30);
}
//page.Response.Cookies[strPage].Expires = DateTime.Now.AddDays(1); //保存一天
}
/// <summary>
/// 参数索引。写入时,如果参数已经存在,则覆盖,否则创建
/// </summary>
/// <param name="ParaName"></param>
/// <returns></returns>
public string this[string ParaName]
{
get
{
if (Paras.ContainsKey(ParaName.ToLower()))
{
return Paras[ParaName.ToLower()];
}
return "";
}
set //有则覆盖,没有就添加
{
if (Paras.ContainsKey(ParaName.ToLower()))
{
Paras[ParaName.ToLower()] = value;
}
else
Paras.Add(ParaName.ToLower(), value);
//回写
page.Response.Cookies[strPage][ParaName.ToLower()] = HttpUtility.UrlEncode(value);
page.Response.Cookies[strPage].Expires = DateTime.Now.AddDays(30);
//page.Response.Cookies[strPage][ParaName.ToLower()] = value;
}
}
/// <summary>
/// querystring中的变量,不存在则返回空
/// </summary>
/// <param name="ParaName"></param>
/// <returns></returns>
public string QueryString(string ParaName)
{
return StringUtil.ToStr(page.Request.QueryString[ParaName]);
}
/// <summary>
/// 所有参数的信息
/// </summary>
/// <returns></returns>
public string AllInfo()
{
string ret="";
foreach (KeyValuePair<string, string> kvp in Paras)
{
ret += kvp.Key + ":" + kvp.Value + "rn";
}
return ret;
}
}
- android常用接口(一)
- 2014密码时代已死?六种旨在取代传统密码位置的新奇想法
- 程序员你为什么这么累【续】:编码习惯之配置规范
- Spring Security (一) Architecture Overview
- Spring Security (二) Guides
- 一个 android 的框架
- Spring Cloud实战小贴士:Ribbon的饥饿加载(eager-load)模式
- android常用接口(二)
- Spring Cloud实战小贴士:Zuul的饥饿加载(eager-load)使用
- RxAndroid完全教程
- 全能型反汇编引擎 – Capstone-Engine
- Hijack攻击揭秘
- 都在说微服务,那么微服务的反模式和陷阱是什么(二)
- Spring Boot 2.0 - WebFlux framework
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Hibernate第二天:Hibernate的一级缓存、其他的API
- pytest 测试框架学习(12):pytest.deprecated_call
- Pinstaller(Python打包为exe文件
- pytest 测试框架学习(14):pytest.warns
- ImportError: /lib64/libm.so.6: version `CXXAB_1.3.8.' not found (required by /usr/local/python37/lib
- pytest 测试框架学习(15):pytest.freeze_includes
- Linux: scp文件,目录上传下载标准版
- Hibernate第三天:Hibernate的一对多配置、Hibernate的多对多的配置
- Git: 掉坑记 -- git reset 杀手
- ModuleNotFoundError: No module named 'phkit.pinyin'
- Hibernate第四天:Hibernate的查询方式、抓取策略
- 爬虫抓取博客园前10页标题带有Python关键字(不区分大小写)的文章
- Python爬虫抓取唐诗宋词
- ImportError: /lib64/libm.so.6: version `GLIBC_2.23' not found (required by /usr/local/python37/lib/p
- 数据分析与数据挖掘 - 02基础操练