我看过无数篇讲解webform相关技术文章,其中不乏优秀的。但是据我看来,大多只是“知其然而不知其所以然”。主要是教你“如何做”,而很少有人会讲解其背后的原理。
这大概和微软的整个技术体系有关。呵呵。当然,这不是本章介绍的重点。本章中,我将借助一个常见的IsPostBack属性,从web本质去讲解,希望能引发读者的思考。
关于IsPostBack的解释,网上一搜,真实铺天盖地的。但我认为,总结的并不完善,而且初学者很容易被搞“晕”。
页面第一次加载:比如在浏览器地址栏中直接敲入“http://.../Index.aspx”并回车,默认是发送的是http get请求,IsPostBack为false
页面第二次加载:选中浏览器地址栏,再回车(或者直接刷新),默认发送的还是get请求,IsPostBack还是为false。反复操作,IsPostBack总是为false所以,纠正一点。网上介乎所有的解释“IsPostBack”时候,都会提到了所谓的“第一次访问”或“第一次加载”,也有一定的道理。
但是,这样解释并不精确,有点牵强(是从MSDN直接机器翻译过来的)。尤其容易让初学者(或者从jsp、php转型过来的)弄晕。
有的初学者甚至会这样认为:我第一次打开index.aspx页面后,再刷新一下,这是第二次访问(页面加载)了,此时IsPostBack应该为true了吧。
我一直习惯从本质去看待技术,个人认为要想真正透彻的理解IsPostBack,必须要明白web应用的本质(例如基础的html、form、http协议等)
本章我就先不借助Reflector工具去反编译Page类和它的IsPostBack属性了,免得弄得有些读者一头雾水。
asp.net webform技术,很适合那些从传统C/S开发模式转型过来的开发人员,尤其是从事Windows桌面编程的,基于控件,事件驱动编程的。
为了提供这种方便,使BS/CS架构融合,其实webform技术幕后做了无数的工作,才使得很多开发人员感觉“asp.net和桌面应用差不多,都是拖控件,设属性,事件...”。
所以,那些不求甚解的“代码工人”,会感觉asp.net很简单,拖拖控件设属性事件绑定数据就好了。也就引发了业内对asp.net程序员的贬低。
相反,对于一开始从事过asp,jsp开发的人(我本人就是)来讲,更习惯从web开发的“请求/处理/响应”模型本质去探索、思考。
言归正传,回到主题,继续讲IsPostBack。先说说啥是“PostBack”吧。webform中所谓的PostBack(回发),其实是先Post然后再Back。啥意思呢?有朋友肯定会疑问了,难道我是用get请求就不行?当然不是了。还是先看图片:
点击之后呢:
看到了吧。我一点submite提交了表单后,页面刷的一下,文本框中中的值又变为空了,页面还原了,表单和按钮都还存在呢。这就是“Back”嘛(服务器端处理请求完成后,重新响应数据传给浏览器端,浏览器再呈现html)。如果你使用纯html,将表单提交给一个ashx程序去处理,ashx中只使用了一行Response.wrtite("Hello world")去响应用户的请求,并输出,你会发现:当你点击按钮之后,页面刷的一下,页面呈现给你的,只有一行"Hello world"了,页面上的表单元素,按钮等都不见了,这显然不是我们想要的效果。而在我们的aspx中,提交请求给后台去处理,后台同样只写了一行Response.wrtite("Hello world"),但结果会是这样:之前的表单、按钮等都存在,而且文本框的值也都空了,并且页面上多出一行“"Helloworld”来。所以,这说明在我们的webform模型中,服务器端在处理完请求之后,又会将之前页面的html数据重新响应给客户端,交给浏览器去渲染,再呈现给用户。这其实是个复杂的过程。明白了吗?webform的背后默默地做了无数的工作,以至于你习惯了觉得理所当然,都没有任何发觉,强大吧?包括webform的事件驱动、后台可以直接控件取值等等,其实都是模拟出来的。用性能消耗换换来方便。
那不用说了,我点击按钮提交表单,这个动作就是“Post”了。当然,这里所说的post,并不是只局限于post请求方式,像上面的例子中就是用的get方式提交表单的,实现了动态web应用的“请求/处理/响应”的流程。搞清楚了PostBack,再说Page类的这个IsPostBack属性,应该就好理解了。
其实IsPostBack表示的意思是“是否回发”。判断“是否回发”说通俗点就是:“你是只为了单纯的处理页面呈现,将动态网页变成html给客户端,还是处理客户端的请求,并做出相应”。说白了,就是是否发生了表单提交的submit动作。web中传统的表单提交动作,get方式请求,默认会将值放入url中,而post方式,默认将值放入form表单内一起提交。
一般情况,是表单提交的动作(且传过来了),不管是post还是get,浏览器客户端有传值过来,服务器端去处理并相应请求,IsPostBack属性则为True。
当用户使用get请求提交表单时,浏览器地址栏中可能会有如下的内容:
这很正常,这是使用webform中使用的viewstate和隐藏域来存值(是base64位编码的)。把这段url拷贝到新的浏览器窗地址栏中,直接按回车,跟踪到后台代码中的Page_Load下面,IsPostBack属性还是true。
但是,如果你去掉或者修改了url中的“viewstate”(改一个字母都不行的), 再按回车,跳到后台去,IsPostBack属性将会变为false。
同样的,你直接在浏览器敲入localhost:1175/ISPostBackDemo.aspx?id=1,这样看起来也是在用URL传值,但这只是个普通的get请求,是不包含表单提交动作,而且没有隐藏域没有viewstate相关的数据。所以,IsPostBack还是false。
而我直接在浏览器中敲入的“.../Index.aspx”,没有表单提交动作,服务端只是负责呈现页面(转换成html),刷洗加载一万次,IsPostBack属性都还是false网友总结如下结论:
结论① 对于使用Server.Transfer进行迁移时迁移到的页面其IsPostBack=false。
结论② Post方式如果Request中没有请求值,即Request.Form =null则IsPostBack=false;Get方式如果Request中没有请求值,即Request.QueryString =null则IsPostBack=false。
结论③ 如果QueryString或Form虽然有请求值,但是QueryString或Form中的Key没有“__VIEWSTATE”和“__EVENTTARGET”和“__VIEWSTATEFIELDCOUNT”,并且没有键为“null”,值以“__VIEWSTATE”开头并且也没有值为“__EVENTTARGET”的键值对,则IsPostBack=false。
结论④ 使用Response.Redirect方式向自画面迁移时,此时IsPostBack=false。
结论⑤ 发生跨页提交(CrossPagePostBack),当访问PreviousPage属性的时候,对于源Page,IsPostBack=true。
结论⑥ 发生跨页提交(CrossPagePostBack)时目标页面是IsPostBack=false
结论⑦ 使用Server.Execute迁移到的页面其IsPostBack=false。
结论⑧ 在Page运行期间其对应的DLL被更新了并且Page的树结构发生过变化,这种情况下请求时IsPostBack=false。
我个人再补充一点:当使用Ajax请求某个aspx页面时(也可以是请求本页面),无论是post还是get方式,默认情况下,IsPostBack总是为false
除非你使用:$(document.forms[0]).serialize() 作为参数传递,该方法能够自动序列化form表单的value,类似一次Post表单提交动作。其实因为提交了“_viewstate”相关的信息给服务器端,所以ispostback属性会变成true。
同理,你在后台使用服务端控件的取值时,如果禁用了viewstate,是无法取到值的。
小提示:Request.Form是指用form递交过来的数据。而Request.QueryString则是指用URL递交过来的。而Request中,包含了所有的请求信息。
还有点很多人都会误解的:当使用Request.Form["aaa"]或者Request["aaa"]去取值时,aaa其实是指的表单元素的name属性,而不是其id。
当使用服务端控件时,只有ID属性,例如 <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
默认情况下,解析成html后,将变成<input name="TextBox1" type="text" id="TextBox1" />。默认情况服务端控件的ID属性和隐藏的ClientID值一样。
但是,当aspx被父级包裹时(例如master模板页),解析成html后的id和name都会发生改变,这时候,ClientID就派上用场了。