开篇:毫无疑问,ASP.Net WebForm是微软推出的一个跨时代的Web开发模式,它将WinForm开发模式的快捷便利的优势移植到了Web开发上,咱们只要学会三步:拖控件→设属性→绑事件,即可以行走于天下。但这样真的就能够走一生吗?实际上,ASP.Net常常被喷的诟病就在于WebForm以及只会拖控件的ASP.Net程序员,每每大型互联网系统也没有采用WebForm的模式进行开发。可是,WebForm并非一无可取,而是咱们没有用好,还有不少东西咱们知其然不知其因此然,如今咱们就来对这些平时所不注意但又十分关键的东西一探究竟。 javascript
在WebForm中,全部的页面请求都是以aspx文件做为请求对象(静态化和伪静态的除外)。例如上图中,访问者在浏览器端经过输入URL:blog/index.aspx向服务器端发送请求,服务器端首先找到这个index.aspx,而后建立页面对象(index.aspx.cs文件中的类对象),调用这个页面对象中的ProcessRequest方法和Page_Load方法(在此过程当中,有可能须要访问数据库)来生成aspx页面的全部html内容,最后将生成好的html返回给浏览器端。 html
所以,咱们能够知道,服务器端对aspx的处理过程其实就是一个渲染生成html的过程。 java
经过实践可知,在aspx中除了<%%>的内容和runat="server"的内容,其余都是原样输出。这是由于咱们在aspx中能够借助<%%>写入C#代码,就跟ASP、PHP同样的风格。可是,在实际开发中并不建议这么来作,由于它违反了CodeBehind的原则,不利于职责的分离。 git
①直接写入C#业务逻辑代码程序员
<% for (int i = 0; i < 5; i++) { Response.Write("I am a webform page.<br/>"); } %>
②获取C#方法的返回值 github
假设页面后端代码中有一个GetServerTime的方法,它只有一句代码:return DateTime.Now.ToString();。页面中只须要经过<%= 方法名() %>便可获取该方法的返回值。web
<%= GetServerTime() %>
③aspx中可以访问的方法的访问修饰符只能为public和protected:这是由于aspx和aspx.cs之间的关系是编译生成后aspx和aspx.cs会建立两个类,而且aspx继承自aspx.cs中的类,在面向对象中子类要访问父类的方法,那么方法的访问修饰符必须为public或protected。(后面会讲到aspx和aspx.cs的关系,不要急) 数据库
ashx是通常处理程序,它是一个实现了IHttpHandler的轻量级处理程序,处理操做都在ProcessRequest方法中完成。 后端
而aspx则至关于一个特殊的、高级的ashx,aspx所对应的父类是System.Web.UI.Page这个类,经过查看Page类的定义,咱们能够看到Page也实现了IHttpHandler的这个接口。另外之因此说它是高级的ashx,是由于aspx帮咱们封装了许多底层的操做,使得咱们能够进行傻瓜式的开发操做。 浏览器
看到这里,咱们不由要问:既然有了ashx为什么还要aspx?你们都知道ashx中的ProcessRequest方法须要向请求响应报文中输出html,而每一个html页内容有不少,若是每次响应都往里边输出html开发起来会很痛苦(这里主要是指在若是不借助模板引擎的状况下),而aspx则起到了相似于于一个模板引擎的做用,帮咱们把html的大致框架定义好了,咱们在开发中就只须要操做每次响应须要更改的内容便可。
(0)假如咱们有如下的名为FirstPage的一个aspx页面:
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码
其后台代码.cs文件代码以下:
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码
(1)CodeBehind:在每一个aspx文件中的头部,咱们都会看到如下的一句代码
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="FirstPage.aspx.cs" Inherits="WebFormDemo.FirstPage" %>
其中CodeBehind这个属性定义了此aspx页面的专属后台代码文件的名称,而Inherits这个属性则定义了此aspx页面所要继承的父类的名称(这也能够简单地说明,aspx页面会单独生成一个类,与后台代码类不重合在一块儿)。所以,aspx.cs就是aspx的后置处理代码,负责处理aspx中<%%>和runat="server"的内容。
(2)子类与父类:咱们使用ASP.NET写的网站在运行时候都会被编译生成为一个一个的程序集(.dll),而咱们的aspx页面也会被生成为一个一个的类。那么,咱们如何来证实aspx会生成一个类,并且仍是aspx.cs中的类的子类呢?那么,咱们须要反编译系统所生成的程序集(.dll)文件。
第一步:找到网站所生成的程序集
咱们能够经过写入如下代码,而后在aspx中<% GetDllInfo(); %>调用;
protected void GetDllInfo() { Response.Write("页面类名称:"+this.GetType() + "<br/>"); Response.Write("程序集地址:"+this.GetType().Assembly.Location + "<br/>"); Response.Write("父类的名称:"+this.GetType().BaseType + "<br/>"); Response.Write("程序集地址:"+this.GetType().BaseType.Assembly.Location + "<br/>"); }
浏览页面,会显示如下结果:经过下图能够看到,咱们的FirstPage这个页面会生成一个ASP.firstpage_asx的类,其父类是FirstPage。
PS:当某个页面第一次被访问的时候,CLR就会使用一个代码生成器去解析aspx文件并生成源代码并编译,而后之后的访问就直接调用编译后的dll,这也是为何aspx第一次访问的时候很是慢的缘由。
第二步:反编译临时程序集文件
①经过上面显示的路径找到dll,并拖到反编译工具(ILSpy或者Reflector,前者开源免费,后者已经收费,但天朝,你懂的。)进行查看。经过下图能够看出,页面类aspx是后台代码类所绑定的子类,它的名称是aspx文件名加上“_aspx”后缀。所以,这里也就解释了为何在aspx中要访问的方法必须是public和protected的访问修饰符才能够。
②下图则展现了对页面后置代码类所在的程序集进行反编译的状况:
第三步:咱们在刚刚时就说了,服务器端对aspx处理的过程是一个渲染生成html的过程,如何来深刻理解这句话,咱们能够在此借助反编译工具来一探究竟。经过对aspx类的反编译,咱们能够看到在它的方法列表中有以下几个命名格式同样的方法:
①_BuildControl_controlX(); X表明数字
经过对这几个方法的源码分析,咱们能够知道,这些方法都在作一件事件:拼接生成aspx页面的html内容。每一个方法都会返回一个控件类型的对象,有LiteralControl类型,也有HtmlHead类型(在aspx中只要给head加了runat="server"就会有此类型的生成方法)等等,那么这些数字又表明了什么?难道是生成html的执行顺序么?这些生成方法又是在哪一个方法中被一一调用的呢?别急,下面咱们就来看看。
②经过后面几个方法源码的查看,咱们发现原来上面的几个生成控件的方法都在一个叫作BuildControlTree的方法(生成控件树)中被依次调用。
这里几乎是按照数字序号的顺序来依次调用具体的BuildControl_controlX()方法,并将每次返回的控件添加到页面中去。这里能够看到,BuildControlTree方法的参数是其自己,它实现了IParserAccessor的接口。这里暂且将这个接口其理解为一个大的控件容器,能够往这个容器里边添加子控件(这里看到不一样类型的控件均可以往里边加,那么确定初步判定方法参数应该是object类型),这里将每次调用BuildControl_controlX()方法所返回的控件类型添加到了这个容器中。
③刚刚分析了BuildControlTree方法,知道了控件的生成过程。可是,页面主体内容又在哪里呢?服务器端要返回的内容可不止是那些控件的HTML代码啊。别急,经过查看反编译的方法,咱们看到原来Renderform1这个方法里边。PS:这里方法名为何是form1呢?那是由于咱们在aspx中给form表单设置的ID就为form1。
④这里咱们就分析到这儿,而WebForm具体的页面生命周期留到后面的ASP.Net页面生命周期探索的文章中详细介绍。这里咱们只须要知道,aspx这个类是其后置代码类的子类,它要作的工做就是帮咱们生成要返回浏览器端的html内容便可。其中,RenderForm将渲染生成整个form表单,而BuildControlTree则会生成服务器控件树,以便在后面的方法中方便地调用每一个控件的RenderControl方法生成html字符串。
企业项目中常用到的最多仍是一些“轻量级”的控件,例如:Button、TextBox、CheckBox、RadioButton、DropDownList、Repeater、ListView等;就我所实习的单位来讲,这一年作WebForm的项目以来,用的最多也就是这些控件,数据控件除了Repeater就没用过其余的。我以为数据控件的话,好好学习下Repeater就够了,由于Repeater已经足够强大了。至于什么***DataSource、Validator、Wizard、Login还有什么ASP.Net AJAX ToolKit就根本没杂用,这些控件既复杂又不实用,并且还比较重量级。
PS:有关Repeater控件的详细学习,能够参考w3school的教程:http://www.w3school.com.cn/aspnet/aspnet_repeater.asp
(1)Button控件中的OnClientClick属性
①在WebForm中,Button控件有两个Click事件:一个是OnClick的服务端事件,另外一个是OnClientClick的客户端事件;OnClick事件写在后置代码类中,每次点击Button首先会触发OnClientClick事件(OnClientClick会返回一个bool值,为true则继续执行OnClick,为false则不继续)。
②经过分析这个属性,能够知道OnClientClick是一个字符串属性,写的代码是JavaScript代码,在上面所说的BuildControl方法中会渲染成input的onclick方法,它会运行在浏览器端。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ClientClickPage.aspx.cs" Inherits="WebFormDemo.ClientClickPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>OnClientClick</title> <script type="text/javascript"> function checkConfirm() { var flag = confirm("您肯定要删除此内容?"); return flag; } </script> </head> <body> <form id="form1" runat="server"> <div> <asp:Label ID="lblFlag" runat="server" Text="This is a label control text."></asp:Label> <asp:Button ID="btnDelete" runat="server" Text="Delete" OnClientClick="return checkConfirm()" onclick="btnDelete_Click" /> </div> </form> </body> </html>
在上面的Button控件中,既设置了OnClientClick也设置了OnClick服务端事件,浏览生成的页面源代码,能够看到在生成的html中,OnClientClick确实是渲染成了input的onclick这个浏览器端的事件:在Button每次以POST方式向服务器提交请求以前,都会先进行checkConfrim这个方法的判断,若是返回值为true才会将请求提交到服务器端;
(2)被某些人滥用的LinkButton
①LinkButton用法跟Button差很少,区别就只在于LinkButton渲染成超连接(<a></a>),而Button渲染生成input标签(<input type="button"></input>)。
②不要用LinkButton来实现普通的超连接,在实际开发中,我还真见过有些人用LinkButton来实现超连接的:他们在LinkButton的OnClick事件中写Response.Redirect("xxx.aspx");这种页面跳转代码,彻底是做死的节奏啊。能在浏览器端进行的事儿为啥要弄到服务器端来进行呢?并且就只是一个页面跳转的小事。
(1)什么是PostBack
好比如今正在访问a.aspx这个页面上,点击页面上的某个submit按钮把数据提交到a.asx.cs进行处理,这个过程则能够看做是:“从客户端浏览器把以前的状态数据提交回来(PostBack)”。
PS:设置了runat="server"的Button或者input控件都会渲染生成type="submit"的按钮
(2)刚刚提到只有点击submit类型的按钮才会提交请求到服务器,那么在如下这种场景如何破呢?
<form id="form1" runat="server"> <div> <asp:DropDownList ID="ddlProvince" runat="server" onselectedindexchanged="ddlProvince_SelectedIndexChanged"> <asp:ListItem Value="BJ">北京市</asp:ListItem> <asp:ListItem Value="CQ">重庆市</asp:ListItem> <asp:ListItem Value="SC">四川省</asp:ListItem> </asp:DropDownList> <asp:DropDownList ID="ddlCity" runat="server"> <asp:ListItem Value="-1">请先选择省份</asp:ListItem> </asp:DropDownList> </div> </form>
有一个省市两级联动的下拉列表场景,在用户选择一个省份后,自动从服务器获取属于该省份的市名下拉列表。这里使用了DropDownList控件,该控件提供了一个叫作SelectIndexChanged的事件,它会帮咱们渲染生成select的onchange的浏览器事件。可是在页面的浏览过程当中,咱们怎么选择不一样的省份,市名称的下拉列表就是不动,由于没有向服务器提交数据请求。
①这时候,一位名叫MSDN的大神会告诉你,须要给这个DropDownList控件设置一个AutoPostBack="true"的属性,经调试后果真可行了。可是,做为一个学习者,咱们会想知道到底这个AutoPostBack帮咱们作了什么事儿?这时,咱们能够经过查看浏览器的源代码一探究竟。
②经过浏览器提供的开发人员工具查看数据请求报文,能够看到除了提交form中的input外,还提交了ASP.Net WebForm预置的一些隐藏字段,而这些隐藏字段则是WebForm为咱们提供便利的基础。好比EventTarget则记录刚刚提交给服务器的是哪一个服务器控件。
注:WebForm页面中若是有一个runat="server"的form,那么一定会涉及到IsPostBack。
(1)Http的无状态:由于Http是无状态的,因此此次会话结束下次再给我提交请求我也不记得你是谁,即便你是李刚的儿子,老子也不认识。那么,为了解决这种问题,咱们可使用一些方法来解决,例如设置一个隐藏字段来判断,若是是PostBack那么确定请求报文中会带上这个字段,若是不是那么请求报文中确定没有这个字段。好比,下面咱们使用隐藏字段来做为判断PostBack的标志。
<input name="IsPostBack" type="hidden" value="true" />
(2)ASP.Net WebForm中内置了一个IsPostBack属性(bool类型),咱们能够在Page_Load事件中判断IsPostBack是否为true,若是不为true则能够知道是第一次访问或者是请求页面的操做,而若是为true则表明是PostBack操做,咱们能够分别进行不一样的业务逻辑处理。例如:有的代码只会在页面第一次加载时才执行(好比从数据库中读取数据并显示),这时就应该使用IsPostBack进行判断。
if (!IsPostBack) { this.lblInfo.Text = "第一次来,不是PostBack"; } else { this.lblInfo.Text = "非第一次来,是PostBack"; }
(3)经过查看生成的页面html代码,咱们没有发现页面中有IsPostBack的这个隐藏字段。那么,它是存储在哪一个位置又是根据什么来判断的呢?实际上,IsPostBack属性是根据ViewState中的一些特殊的键值对来判断赋值的(由于:每次提交请求后,服务器端都会返回不一样的ViewState隐藏域给浏览器端;一样,浏览器每次也会将ViewState提交给服务器端,服务器端会解析ViewState还原上次状态)。对于ViewState,能够经过一些软件例如ViewStateDecoder进行解析查看,例以下图:
若是咱们禁用了ViewState,那么也就没法正常使用IsPostBack属性了,也没法正常使用PostBack了。那么对于ViewState,我会在下一篇进行简单探秘,本篇就到此为止。
若是你以为本文对你有用,那就麻烦点个“推荐”吧,也能让我更有动力写下去,谢谢!
(1)反编译利器ILSpy:https://github.com/icsharpcode/ILSpy/releases/download/2.2/ILSpy_2.2.0.1706_Binaries.zip
(2)aspx揭秘的WebFormDemo:http://pan.baidu.com/s/1ntqOl4H
(3)服务器控件揭秘WebFormDemo:http://pan.baidu.com/s/1qWFK8cW
做者:周旭龙
出处:http://edisonchou.cnblogs.com
本文版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文连接。