对ASP.Net的认识(三)
较为详细的整个请求处理流程
Application: BeginRequest
Application: PreAuthenticateRequest
Application: AuthenticateRequest
Application: PostAuthenticateRequest
Application: PreAuthorizeRequest
Application: AuthorizeRequest
Application: PostAuthorizeRequest
Application: PreResolveRequestCache
Application: ResolveRequestCache
Application: PostResolveRequestCache
Application: PreMapRequestHandler
Page: Construct
Application: PostMapRequestHandler
Application: PreAcquireRequestState
Application: AcquireRequestState
Application: PostAcquireRequestState
Application: PreRequestHandlerExecute
Page: AddParsedSubObject
Page: CreateControlCollection
Page: AddedControl
Page: AddParsedSubObject
Page: AddedControl
Page: ResolveAdapter
Page: DeterminePostBackMode
Page: PreInit
Control: ResolveAdapter
Control: Init
Control: TrackViewState
Page: Init
Page: TrackViewState
Page: InitComplete
Page: LoadPageStateFromPersistenceMedium
Control: LoadViewState
Page: EnsureChildControls
Page: CreateChildControls
Page: PreLoad
Page: Load
Control: DataBind
Control: Load
Page: EnsureChildControls
Page: LoadComplete
Page: EnsureChildControls
Page: PreRender
Control: EnsureChildControls
Control: PreRender
Page: PreRenderComplete
Page: SaveViewState
Control: SaveViewState
Page: SaveViewState
Control: SaveViewState
Page: SavePageStateToPersistenceMedium
Page: SaveStateComplete
Page: CreateHtmlTextWriter
Page: RenderControl
Page: Render
Page: RenderChildren
Control: RenderControl
Page: VerifyRenderingInServerForm
Page: CreateHtmlTextWriter
Control: Unload
Control: Dispose
Page: Unload
Page: Dispose
Application: PostRequestHandlerExecute
Application: PreReleaseRequestState
Application: ReleaseRequestState
Application: PostReleaseRequestState
Application: PreUpdateRequestCache
Application: UpdateRequestCache
Application: PostUpdateRequestCache
Application: EndRequest
Application: PreSendRequestHeaders
Application: PreSendRequestContent
Master Page的加载顺序
1、Master page中的用户控件的page_init
2、aspx页面中的用户控件的page_init
3、Master page的page_init
4、aspx页面的page_init
5、aspx页面的page_load
6、Master page的page_load
7、Master page中的用户控件的page_load
8、aspx页面中的用户控件的page_load
其实Master Page可以看成是一个控件。
何时使用DataGrid/DataList/Repeater
实现了IEnumerable接口的类的对象可以成为这三种数据控件的数据源。
DataGrid最终会生成一个table,所以DataGridItem类继承自TableRow类。而Repeater最终生成的控件是用户自定义的,所以RepeaterItem类与TableRow类没有任何关系。DataGrid和DataList继承自WebControl类,而Repeater继承自Control类,WebControl类包含一些设置样式的属性,如BackColor,ForeColor,CssStyle,BorderStyle等,而Repeater样式的设置必须在它的Template里完成。
DataGrid有很多功能但欠缺灵活性,使用DataGrid很便捷,因为很多功能都已经封装好,只需要设置下相应的属性即可,但也正因为这个好处,使得DataGrid比较呆板,只能使用已经做好的功能,修改现有功能会比较麻烦,而且DataGrid最终生成的是一个table,如果想生成多个table或者div,DataGrid就无能为力了。DataGrid的性能是最差的因为DataGrid会把几乎所有内容都放入它的ViewState中,如果要使用DataGrid的Sort,Paging和Edit功能就不能禁掉ViewState,这会使ViewState大到足以严重影响性能的程度。
DataList比起DataGrid更加灵活,它可以使一行有多条记录,既可以使用table也可以使用span,不过有许多功能DataList并没有实现,需要开发人员自己实现,这样虽然会让开发人员花费更多时间但增强了灵活性,DataList的性能比起DataGrid要好。
Repeater是最灵活的,但对开发人员的要求也是最高的,它会花费最多的开发时间,但可以实现很多DataGrid和DataList无法实现的效果和功能,而且性能是最好的。
Page继承自TemplateControl并且实现了IHttpHandler接口,TemplateControl继承自Control,Page本身就是个HttpHandler。
Page处理Request的流程大致如下代码所述,也就是页面处理的流程。
private void ProcessRequestMain(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint)
{
// 1. PreInit
this.PerformPreInit();
// 2. Init
this.InitRecursive(null);
this.OnInitComplete(EventArgs.Empty);
// 对于Postback,插入下面处理
if (this.IsPostBack)
{
// 加载ViewState和ControlState,进行场景恢复
this.LoadAllState();
// 第一次处理PostData
this.ProcessPostData(this._requestValueCollection, true);
}
// 3. PreLoad
this.OnPreLoad(EventArgs.Empty);
// 4. Load
this.LoadRecursive();
// 对于Postback,插入下面处理
if (this.IsPostBack)
{
// 第二次处理PostData
this.ProcessPostData(this._leftoverPostData, false);
// 如果PostData表面某个Control数据发生变化,那么RaisePostDataChanged事件
this.RaiseChangedEvents();
// RaisePostBackEvent
this.RaisePostBackEvent(this._requestValueCollection);
}
this.OnLoadComplete(EventArgs.Empty);
// 对于CallBack,RaiseCallBackEvent
if (this.IsPostBack && this.IsCallback)
{
this.PrepareCallback(callbackControlID);
}
else if (!this.IsCrossPagePostBack)
{
// 5. PreRender
this.PreRenderRecursiveInternal();
}
// 对于CallBack, Render出CallBack的结果
if (this.IsCallback)
{
this.RenderCallback();
}
else if (!this.IsCrossPagePostBack)
{
this.PerformPreRenderComplete();
// 6. SaveViewStae和ControlState
this.SaveAllState();
this.OnSaveStateComplete(EventArgs.Empty);
// 7. Render 整个Control
this.RenderControl(this.CreateHtmlTextWriter(this.Response.Output));
}
}
其中,PerformPreInit方法代码如下,可以发现PreInit时主要初始化Themes,加载MasterPage。
private void PerformPreInit()
{
this.OnPreInit(EventArgs.Empty);
this.InitializeThemes();
this.ApplyMasterPage();
this._preInitWorkComplete = true;
}
Page以及每个Control都有个ControlState属性,该属性用于记录Page处理到哪个阶段了,例如Init阶段结束时,这个属性会被赋值ControlState.Initialized,从而表明Init阶段的结束。这个属性会被用于很多地方,例如当添加一个动态控件时,父控件的AddedControl方法就会检查这个属性,如果已经过了Init阶段,则直接初始化添加的控件。ControlState和ViewState最大的区别是ControlState是无法被禁止的,并且ControlState里有个ArrayList,这个ArrayList保存了需要处理PostBackData的Control的ID,当处理PostBackData时就会从这个ArrayList里获取需要处理的Control的ID。
下面是Control.AddedControl方法的代码,
protected internal virtual void AddedControl(Control control, int index)
{
// 1. 初始化Page,Parent,NameContainer,ID
control._parent = this;
control._page = this.Page;
control.flags.Clear(0x20000);
Control namingContainer = this.flags[0x80] ? this : this._namingContainer;
if (namingContainer != null)
{
control.UpdateNamingContainer(namingContainer);
if ((control._id == null) && !control.flags[0x40])
{
control.GenerateAutomaticID();
}
else if ((control._id != null) || ((control._occasionalFields != null) && (control._occasionalFields.Controls != null)))
{
namingContainer.DirtyNameTable();
}
}
// 2. 判断当前Control所在的页面生命周期阶段,然后对于新加入的Control进行补充调用
if (this._controlState >= ControlState.ChildrenInitialized)
{
control.InitRecursive(namingContainer);
if (((control._controlState >= ControlState.Initialized) && (control.RareFields != null)) && control.RareFields.RequiredControlState)
{
this.Page.RegisterRequiresControlState(control);
}
if (this._controlState >= ControlState.ViewStateLoaded)
{
object savedState = null;
if ((this._occasionalFields != null) && (this._occasionalFields.ControlsViewState != null))
{
savedState = this._occasionalFields.ControlsViewState[index];
if (this.LoadViewStateByID)
{
control.EnsureID();
savedState = this._occasionalFields.ControlsViewState[control.ID];
this._occasionalFields.ControlsViewState.Remove(control.ID);
}
else
{
savedState = this._occasionalFields.ControlsViewState[index];
this._occasionalFields.ControlsViewState.Remove(index);
}
}
control.LoadViewStateRecursive(savedState);
if (this._controlState >= ControlState.Loaded)
{
control.LoadRecursive();
if (this._controlState >= ControlState.PreRendered)
{
control.PreRenderRecursiveInternal();
}
}
}
}
}
Control的卸载也是递归调用的。
对于需要用ASP.Net进行身份验证的页面,即使所有内容是静态的,也需要将后缀名改成aspx而不能用html,这是因为html将不会被ASP.Net的ISAPI所处理,也就无法进行身份验证。
aspx页面一般有ASP.Net自带的页处理程序来处理,它是Http处理程序中的一种,另外还有处理asmx的Web服务处理程序,如果需要针对特别的后缀名进行特别处理,需要创建一个自定义的Http处理程序,然后在IIS中将扩展名映射到ASP.Net的ISAPI中,并且在Web.config中注册这个处理程序。在Web.config中的注册如下代码所示
<configuration>
<system.web>
<httpHandlers> <add verb="*" path="*.sample" type="MyHandler"/></httpHandlers>
</system.web>
</configuration>
创建一个自定义的Http处理程序,有两种方式,
1、实现IHttpHandler接口创建同步处理程序
2、实现IHttpAsyncHandler接口创建异步处理程序
这个两个接口都需要实现IsReusable属性和ProcessRequest方法。IsReusable属性用于确定HttpHandlerFacotry对象(接收请求并调用相应的处理程序的对象)是否可以把该处理程序放入池中以便重复使用来提高性能,如果不可以的话,那么每次都要创建一个该处理程序的实例。ProcessRequest方法用于处理单个Http请求。如果Http处理程序的扩展名为ashx,那么这个处理程序会自动注册到IIS和ASP.Net中。异步处理程序可以启动一个外部进程,ASP.Net会将用于外部进程的线程放回线程池中以提高性能,直至外部进程处理结束进行回调为止。异步处理程序还必须实现BeginProcessRequest和EndProcessRequest方法。
另外可以通过实现IHttpHandlerFactory接口来自定义HttpHandlerFactory,这样做更具有灵活性,可以根据Http请求的具体命令(HttpContext.Request.RequestType,如PUT,GET,POST等)做一些处理,例如对于不同的命令调用不同的处理程序。在创建了自定义的HttpHandlerFactory以后需要在Web.config和IIS中进行注册。在Web.config中添加如下代码
<configuration>
<system.web>
<httpHandlers> <add verb="GET,POST" path="*.sample" type="MyHandlerFactory" /> </httpHandlers>
</system.web>
</configuration>
在IIS中需要对文件后缀名与ASP.Net的ISAPI进行映射。
ApplicationManager类在asp.net 2.0及之后版本用于创建并管理AppDomain。