作者:周旭龙

出处:http://edisonchou.cnblogs.com

开篇:毫无疑问,ASP.Net WebForm是微软推出的一个跨时代的Web开发模式,它将WinForm开发模式的快捷便利的优点移植到了Web开发上,我们只要学会三步:拖控件→设属性→绑事件,便可以行走于天下。但这样真的就可以走一辈子吗?实际上,ASP.Net经常被喷的诟病就在于WebForm以及只会拖控件的ASP.Net程序员,往往大型互联网系统也没有采用WebForm的模式进行开发。但是,WebForm并不是一无是处,而是我们没有用好,还有很多东西我们知其然不知其所以然,现在我们就来对这些平时所不注意但又十分关键的东西一探究竟。

一、神秘不神秘—aspx探秘

1.1 WebForm时代的请求对象

  在WebForm中,所有的页面请求都是以aspx文件作为请求对象(静态化和伪静态的除外)。例如上图中,访问者在浏览器端通过输入URL:blog/index.aspx向服务器端发送请求,服务器端首先找到这个index.aspx,然后创建页面对象(index.aspx.cs文件中的类对象),调用这个页面对象中的ProcessRequest方法和Page_Load方法(在此过程中,有可能需要访问数据库)来生成aspx页面的所有html内容,最后将生成好的html返回给浏览器端。

  因此,我们可以知道,服务器端对aspx的处理过程其实就是一个渲染生成html的过程。

1.2 神奇的<%%>

  通过实践可知,在aspx中除了<%%>的内容和runat="server"的内容,其他都是原样输出。这是因为我们在aspx中可以借助<%%>写入C#代码,就跟ASP、PHP一样的风格。但是,在实际开发中并不建议这么来做,因为它违反了CodeBehind的原则,不利于职责的分离。

  ①直接写入C#业务逻辑代码

1         <%
2 for (int i = 0; i < 5; i++)
3 {
4 Response.Write("I am a webform page.<br/>");
5 }
6 %>

  ②获取C#方法的返回值

  假设页面后端代码中有一个GetServerTime的方法,它只有一句代码:return DateTime.Now.ToString();。页面中只需要通过<%= 方法名() %>即可获取该方法的返回值。

<%= GetServerTime() %>

  ③aspx中能够访问的方法的访问修饰符只能为public和protected:这是因为aspx和aspx.cs之间的关系是编译生成后aspx和aspx.cs会创建两个类,并且aspx继承自aspx.cs中的类,在面向对象中子类要访问父类的方法,那么方法的访问修饰符必须为public或protected。(后面会讲到aspx和aspx.cs的关系,不要急)

1.3 aspx与ashx的关系

  ashx是一般处理程序,它是一个实现了IHttpHandler的轻量级处理程序,处理操作都在ProcessRequest方法中完成。

  而aspx则相当于一个特殊的、高级的ashx,aspx所对应的父类是System.Web.UI.Page这个类,通过查看Page类的定义,我们可以看到Page也实现了IHttpHandler的这个接口。另外之所以说它是高级的ashx,是因为aspx帮我们封装了许多底层的操作,使得我们可以进行傻瓜式的开发操作。

  看到这里,我们不禁要问:既然有了ashx为何还要aspx?大家都知道ashx中的ProcessRequest方法需要向请求响应报文中输出html,而每个html页内容有很多,如果每次响应都往里边输出html开发起来会很痛苦(这里主要是指在如果不借助模板引擎的情况下),而aspx则起到了类似于于一个模板引擎的作用,帮我们把html的大体框架定义好了,我们在开发中就只需要操作每次响应需要更改的内容即可。

1.4 aspx与aspx.cs的关系

  (0)假如我们有以下的名为FirstPage的一个aspx页面:

 View
Code

  其后台代码.cs文件代码如下:

 View
Code

  (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字符串。

二、好用不好用—服务器控件探秘

2.1 企业中到底在用哪些控件?

  企业项目中经常使用到的最多还是一些“轻量级”的控件,例如: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

2.2 需要注意的基本控件用法

  (1)Button控件中的OnClientClick属性

  ①在WebForm中,Button控件有两个Click事件:一个是OnClick的服务端事件,另一个是OnClientClick的客户端事件;OnClick事件写在后置代码类中,每次点击Button首先会触发OnClientClick事件(OnClientClick会返回一个bool值,为true则继续执行OnClick,为false则不继续)。

  ②通过分析这个属性,可以知道OnClientClick是一个字符串属性,写的代码是JavaScript代码,在上面所说的BuildControl方法中会渲染成input的onclick方法,它会运行在浏览器端。

 View
Code

  在上面的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");这种页面跳转代码,完全是作死的节奏啊。能在浏览器端进行的事儿为啥要弄到服务器端来进行呢?而且就只是一个页面跳转的小事。

2.3 AutoPostBack的那点事

  (1)什么是PostBack

  比如现在正在访问a.aspx这个页面上,点击页面上的某个submit按钮把数据提交到a.asx.cs进行处理,这个过程则可以看作是:“从客户端浏览器把之前的状态数据提交回来(PostBack)”。

PS:设置了runat="server"的Button或者input控件都会渲染生成type="submit"的按钮

  (2)刚刚提到只有点击submit类型的按钮才会提交请求到服务器,那么在以下这种场景如何破呢?

 View
Code

  有一个省市两级联动的下拉列表场景,在用户选择一个省份后,自动从服务器获取属于该省份的市名下拉列表。这里使用了DropDownList控件,该控件提供了一个叫做SelectIndexChanged的事件,它会帮我们渲染生成select的onchange的浏览器事件。但是在页面的浏览过程中,我们怎么选择不同的省份,市名称的下拉列表就是不动,因为没有向服务器提交数据请求。

  ①这时候,一位名叫MSDN的大神会告诉你,需要给这个DropDownList控件设置一个AutoPostBack="true"的属性,经调试后果然可行了。但是,作为一个学习者,我们会想知道到底这个AutoPostBack帮我们做了什么事儿?这时,我们可以通过查看浏览器的源代码一探究竟。

  ②通过浏览器提供的开发人员工具查看数据请求报文,可以看到除了提交form中的input外,还提交了ASP.Net WebForm预置的一些隐藏字段,而这些隐藏字段则是WebForm为我们提供便利的基础。比如EventTarget则记录刚刚提交给服务器的是哪个服务器控件。

2.4 为什么需要IsPostBack

  注: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,我会在下一篇进行简单探秘,本篇就到此为止。

ASP.Net WebForm学习笔记:一、aspx与服务器控件探秘的更多相关文章

  1. 路由其实也可以很简单-------Asp.net WebAPI学习笔记(一) ASP.NET WebApi技术从入门到实战演练 C#面向服务WebService从入门到精通 DataTable与List<T>相互转换

    路由其实也可以很简单-------Asp.net WebAPI学习笔记(一)   MVC也好,WebAPI也好,据我所知,有部分人是因为复杂的路由,而不想去学的.曾经见过一位程序猿,在他MVC程序中, ...

  2. [ASP.NET MVC] ASP.NET Identity学习笔记 - 原始码下载、ID型别差异

    [ASP.NET MVC] ASP.NET Identity学习笔记 - 原始码下载.ID型别差异 原始码下载 ASP.NET Identity是微软所贡献的开源项目,用来提供ASP.NET的验证.授 ...

  3. ASP.NET MVC学习笔记-----Filter2

    ASP.NET MVC学习笔记-----Filter(2) 接上篇ASP.NET MVC学习笔记-----Filter(1) Action Filter Action Filter可以基于任何目的使用 ...

  4. ASP.NET MVC学习笔记-----Filter

    ASP.NET MVC学习笔记-----Filter(1) Filter类型 接口 MVC的默认实现 Description Authorization IAuthorizationFilter Au ...

  5. ASP.NET MVC学习笔记-----Filter(2)

    接上篇ASP.NET MVC学习笔记-----Filter(1) Action Filter Action Filter可以基于任何目的使用,它需要实现IActionFilter接口: public ...

  6. ASP.NET MVC 学习笔记-2.Razor语法 ASP.NET MVC 学习笔记-1.ASP.NET MVC 基础 反射的具体应用 策略模式的具体应用 责任链模式的具体应用 ServiceStack.Redis订阅发布服务的调用 C#读取XML文件的基类实现

    ASP.NET MVC 学习笔记-2.Razor语法   1.         表达式 表达式必须跟在“@”符号之后, 2.         代码块 代码块必须位于“@{}”中,并且每行代码必须以“: ...

  7. ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则

    ASP.NET MVC 学习笔记-7.自定义配置信息   ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...

  8. ASP.Net WebForm温故知新学习笔记:一、aspx与服务器控件探秘

    开篇:毫无疑问,ASP.Net WebForm是微软推出的一个跨时代的Web开发模式,它将WinForm开发模式的快捷便利的优点移植到了Web开发上,我们只要学会三步:拖控件→设属性→绑事件,便可以行 ...

  9. 【转载】ASP.Net WebForm温故知新学习笔记:一、aspx与服务器控件探秘

    开篇:毫无疑问,ASP.Net WebForm是微软推出的一个跨时代的Web开发模式,它将WinForm开发模式的快捷便利的优点移植到了Web开发上,我们只要学会三步:拖控件→设属性→绑事件,便可以行 ...

随机推荐

  1. WPF样式——多条件触发器

    希望创建多个条件都为真时才激发的触发器,就需要使用MultiTrigger提供的Condition集合 <Window x:Class="Styles.MultiTrigger&quo ...

  2. asp.net 生成PDF方法

    今天转博客园看到有人发表了一篇生成PFd的文章,准备自己也留一份准备以后用到的时候方便调用: 首先去itextsharp网站下载控件(https://sourceforge.net/projects/ ...

  3. 在 SQL Server 中的网络数据库文件的支持说明

    其实就是一个学员问SQL Server 是否能存放的于NAS(UAC 的路径下). 官方的回答简略版本为:可以,需要满足一些强制性的硬件要求.但需要考虑一系列的性能的问题. http://suppor ...

  4. 数据的增量更新之EXISTS

    有时候需要实现是数据的增量更新,因为更新全量会带来时间跟数据库资源的浪费,还有可能是数据出现冗余,所以需要使用增量数据同步,下面是一个数据增量同步的小实例. ---drop table A CREAT ...

  5. Hadoop2

    http://www.cnblogs.com/miaoxiaoyu/archive/2012/07/29/2614060.html

  6. unity项目实现“再按一次退出程序”提示功能

    unity项目,再按一次退出程序,按第一次做提示,再按一次,程序退出. float _waitTime = 2f;//前后两次按退出间隔时间 void OnGUI() { ) { GUI.Label( ...

  7. TCP相关知识

    1. TCP与TCP/IP协议族 TCP是TCP/IP协议族中运输层的一个协议.TCP/IP,即传输控制协议/网间协议,是一个工业标准的协议集,包含了运输层.网络层和链路层的协议,其结构如下图所示:其 ...

  8. 【转】java获取当前路径的几种方法

    1.利用System.getProperty()函数获取当前路径: System.out.println(System.getProperty("user.dir"));//use ...

  9. Asp.net弹出层并且有遮罩层

    长久以来,asp.net弹出层并且有遮罩层问题都是一个难以解决的问题,鉴于此,我决定写个弹出层发布出来,供大家使用... 这里的doing层是遮罩层,divLogin层是登陆层 若有其他问题请留言或邮 ...

  10. DevExpress GridControl使用(转)

    DevExpress GridControl使用 (一)原汁原味的表格展示 Dev控件中的表格控件GridControl控件非常强大.不过,一些细枝末节的地方有时候用起来不好找挺讨厌的.使用过程中,多 ...