我们上网时,在浏览器地址输入网址,按下回车,一张网页就呈现在我们眼前。这究竟发生了什么?对于一名优秀的Programmer来说,我想有必要一下熟悉浏览器--->服务器请求的过程。
ASP.NET是运行在公共语言运行时刻时(CLR)上的应用程序框架。他用来在服务器端构建功能强大的web应用程序。当浏览器请求 ASP.NET 文件时,IIS 会把该请求传递给服务器上的 ASP.NET 引擎,ASP.NET 引擎会逐行地读取该文件,并执行文件中的脚本,最后,ASP.NET 文件会以纯 HTML 的形式返回浏览器。 客户端浏览器和服务器之间的请求响应是通过Socket进行通信,基于HTTP协议,客户端发送一次HTTP请求,服务器接收到请求,处理之后向浏览器回应响应报文。那么什么是HTTP协议呢?
当浏览器寻找到Web服务器地址后,浏览器将帮助我们把对服务器的请求转换为一系列参数(消息)发给Web服务器,浏览器和Web服务器的对话中,需要使用双方都能理解语法规范进行通信,这种程序之间进行通信的语法规定,我们称之为协议。浏览器与服务器之间的协议是应用层协议,当前遵循的协议是HTTP/1.1。HTTP/1.1协议时Web开发的基础,这是一个无状态协议,客户端浏览器和服务器通过Socket通信进行请求和响应完成一次会话。每次会话中,通信双方发送的数据称为消息,分为两种:请求消息和响应消息。 对于消息而言,一般他有三部分组成,并且消息的头和消息体之间用一个空行进行分隔:
我们通过浏览器插件HttpWatch Professional可以清晰看到浏览器和服务器之间的通信内容:
了解了什么是HTTP协议之后,我们在回到先前提出的那个问题,浏览器的请求怎样到达服务器?
我们知道要访问一个网站,必须要其部署在相应服务器软件上(如IIS),浏览器向服务器发送请求之后,当请求通过Socket到达服务器时,首先服务器Windows内核中的HTTP.SYS组件捕获请求,根据URL的请求地址将其转发到应用程序池(Application Pool,ASP.NET应用程序必须运行在一个应用程序池中),再由运行在应用程序池里的工作者进程(Worker Process,用于装载专门处理ASP.NET页面的一个ISAPI扩展程序:aspnet_isapi.dll)响应请求,当请求处理完成时,HTTP.SYS又将结果发送出去(HTTP.SYS会在内部建立一个缓存区,用于缓存近期的处理结果)。当HTTP.SYS请求分析这是一个需要交给IIS服务器处理的HTTP请求时,HTTP.SYS组件就会把这次请求交给IISl处理,服务器软件(IIS)会判断用户请求的是静态页面(Html)还是动态页面(Aspx.Ashx),如果请求的是Html静态页面或者js,css,xml以及图片等,IIS直接返回请求的Html静态页面和js等相应文件。那么如果请求的是动态页面呢?还是向处理静态页面一样吗?显然是不可能的。IIS服务器会分析请求的类型,然后从处理程序映射(即下文IIS服务器扩展)表中去匹配,当在处理程序映射表中能够匹配到请求的类型时,那么IIS服务器就将请求交给处理程序映射表中所对应的程序来处理。当IIS发现,在处理程序映射表中没有能匹配的项的时候,就直接返回请求所对应物理路径下的文件,如Html,JS,CSS,JPG,PNG等。
由于IIS服务器在设计时引入了开放的ISAPI接口标准,具备极高的可扩展性。在核心组件不变的情况下可灵活支持不同类型不同版本的ASP.NET应用程序。
ISAPI(Internet Server Application Programming Interface)
ISAPI(服务器应用编程接口),它为开发人员提供了强大的可编程能力,只要按照标准接口开发不同类型的Web应用程序的ISAPI扩展程序,就能实现对IIS功能上的扩展,从而使IIS可以处理不同类型的客户端请求。IIS管理器提供了应用程序配置功能,可以对不同的客户端请求配置不同的ISAPI扩展程序ISAPI扩展程序通常以DLL形式存在,可以被IIS加载并调用。有了基于ISAPI的扩展扩展程序,IIS服务器就可以根据客户端请求的资源扩展名,来决定应由哪个ISAPI扩展程序来处理客户端请求,然后就可以将请求转发给合适的ISAPI扩展程序。
实际上客户发起的请求最终要由aspnet_isapi.dll(被工作者进程Worker Process装载)传递给aspnet_wp.exe去处理,.NET平台下称其为ASP.NET Process(简称为WP),该文件位于.Net Framework安装目录下,与aspnet_isapi.dll所在位置相同。当aspnet_isapi接收到IIS转发的ASP.NET请求后,会将请求放入队列,并根据实际情况分配请求处理任务给WP进程。一旦请求被转送给WP进程,WP进程便会通知aspnet_isapi请求正在被处理。这个通知的过程是通过同步I/O完成的,这么实现目的是为了保证处理过程的完整性,因为只有当请求在aspnet_isapi内部被标记为"executing"后,WP才会真正开始处理该请求。此后请求便在WP的上下文环境中执行。当执行结束后处理结果会通过一个异步的开放管道回送给aspnet_isapi,这时请求的状态会被更新为“Done”。接着请求就会从队列中清除。如果WP进程崩溃,所有正在处理中的请求都将维持“executing”状态一段时间,等到aspnet_isapi检测到WP进程死掉后,会自动丢弃所有的请求并释放已经分配的资源。 WP会分析每一个请求的信息解析出其中的虚拟目录信息,并检查该虚拟目录对应的AppDomain(应用程序域)是否已经存在,如果不存在,则创建一个新的AppDomain(ApplicationManager创建应用程序域),然后使用它。否则直接重用已经建立的AppDomain对象。这里的AppDomain指的是.NET中引入的应用程序域的概念,程序集管理的最小逻辑单位为应用程序域,包括四个重要的机制,隔离、卸载、安全、配置,它可以理解为一个进程或一个边界或一个容器,它是应用程序的执行环境.NET下所有的应用程序都运行在AppDomain中,每一个ASP.NET应用程序IIS中的站点或者虚拟目录都会有一个AppDomain与之对应,它保存了Applcation对象、Cache等全局变量。
当aspnet_wp.exe接受到aspnet_isapi.dll的请求后,就将请求转给指定虚拟目录对应的AppDomain中的ISAPIRuntime对象,ISAPIRuntime.ProcessRequest()开始进入ASP.NET,并将浏览器发送请求消息封装成HttpWorkerRequest类(抽象类,开发环境中对应SimpleWorkRequest)。之后再执行HttpRuntime的静态方法:ProcessRequestNoDemand(参数为封装了浏览器请求的信息:
HttpWorkerRequest)
在Asp.Net中,准备用于处理的请求,必须封装为HttpWorkerRequest类型的对象,这是一个抽象类: [ComVisibleAttribute(false)]
public abstract class HttpWorkerRequest
客户的请求首先会被ISAPIRuntme对象ProcessRequest方法处理
创建了HttpWorkerRequest 类型的wr对象,因为ISAPIWorkerRequest 继承于HttpWorkerRequest
[SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)] public int ProcessRequest(IntPtr ecb, int iWRType) { IntPtr zero = IntPtr.Zero; if (iWRType == 2) { zero = ecb; ecb = UnsafeNativeMethods.GetEcb(zero); } ISAPIWorkerRequest wr = null; try { bool useOOP = iWRType == 1; wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP); wr.Initialize(); ...... if ((appDomainAppPathInternal == null) || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal)) { HttpRuntime.ProcessRequestNoDemand(wr); return 0; } HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString("Hosting_Phys_Path_Changed", new object[] { appDomainAppPathInternal, appPathTranslated })); return 1; } ...... } |
---|
HttpRuntime调用ProcessRequestNoDemand方法:
internal static void ProcessRequestNoDemand(HttpWorkerRequest wr)
{
RequestQueue queue = _theRuntime._requestQueue;
wr.UpdateInitialCounters();
if (queue != null)
{
wr = queue.GetRequestToExecute(wr);
}
if (wr != null)
{
CalculateWaitTimeAndUpdatePerfCounter(wr);
wr.ResetStartTime();
ProcessRequestNow(wr);
}
}
//该方法先从请求队列中取出一个请求,然后更新请求的引用计数器的信息,然后ProcessRequestNow方法处理请求。
//在这儿终于找到了HttpRuntime这个对象了:
internal static void ProcessRequestNow(HttpWorkerRequest wr) { _theRuntime.ProcessRequestInternal(wr); } |
---|
// _theRuntime就是HttpRuntime类型的对象,他在HttpRuntime的静态构造函数初始化。 static HttpRuntime() { ...... _theRuntime = new HttpRuntime(); _theRuntime.Init(); AddAppDomainTraceMessage("HttpRuntime::cctor*"); } |
---|
点击进入ProcessRequsetNow(Wr)方法,Wr即封装了HTTP Message的HttpWorkRequest对象 在HttpRuntime接受到请求后,立刻通过HttpWorkerRequest传递的参数进行分析和分解,创建方便用户网站应用程序处理用的对象。HttpRequest,HttpResponse 终于发现了HttpContext,根据HttpWorkerRequest初始化HttpContext
private void ProcessRequestInternal(HttpWorkerRequest wr) { ...... else { HttpContext context; try { context = new HttpContext(wr, false); } catch { try { wr.SendStatus(400, "Bad Request"); wr.SendKnownResponseHeader(12, "text/html; charset=utf-8"); byte[] data = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>"); wr.SendResponseFromMemory(data, data.Length); wr.FlushResponse(true); wr.EndOfRequest(); return; } finally { Interlocked.Decrement(ref this._activeRequestCount); } } wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, context); HostingEnvironment.IncrementBusyCount(); try { try { this.EnsureFirstRequestInit(context); } catch { if (!context.Request.IsDebuggingRequest) { throw; } } ...... } } |
---|
在进入看看:根据WR,初始化了请求参数的类型HttpRequest对象和处理回应类型HttpReponse对象
internal HttpContext(HttpWorkerRequest wr, bool initResponseWriter) { this._timeoutStartTimeUtcTicks = -1; this._timeoutTicks = -1; this._threadAbortOnTimeout = true; this.ThreadContextId = new object(); this._wr = wr; this.Init(new HttpRequest(wr, this), new HttpResponse(wr, this)); if (initResponseWriter) { this._response.InitResponseWriter(); } PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING); } |
---|
privatevoid ProcessRequestInternal(HttpWorkerRequest wr) ProcessRequestInternal这个方法很重要,前面分析了它创建了上下文对象HttpContext,接下来分析HttpApplication的创建。
{
.....
IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);
......
try
{
this.EnsureFirstRequestInit(context);
}
...... context.Response.InitResponseWriter();
......if (applicationInstance is IHttpAsyncHandler)
{
IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
context.AsyncAppHandler = handler2;
handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);
}
......
}
}
}
EnsureFirstRequestInit()方法完成第一次请求初始化工作,该方法锁定全局变量_beforeRequestFirst,然后调用FirstRequestInit(context)完成配置文件的加载,初始化请求队列,装载Bin目录下所有程序集工作,然后更新_beforeRequestFirst=false;context.FirstRequest=true;
private void EnsureFirstRequestInit(HttpContext context)
{
if (this._beforeFirstRequest)
{ lock (this)
{
if (this._beforeFirstRequest)
{
this._firstRequestStartTime = DateTime.UtcNow;
this.FirstRequestInit(context);
this._beforeFirstRequest = false;
context.FirstRequest = true;
}
}
}
}
看一下HttpApplication这个类的申明:
[ToolboxItem(false)]
public class HttpApplication : IComponent, IDisposable, IHttpAsyncHandler,
IHttpHandler, IRequestCompletedNotifier, ISyncContext
{
}
调用HttpApplicationFactory对象的GetNormalApplicationInstance得到一个HttpApplication实例:
internal static IHttpHandler GetApplicationInstance(HttpContext context)
{
......return _theApplicationFactory.GetNormalApplicationInstance(context);
}
GetApplicationInstance方法生成一个默认的HttpApplication对象,HttpApplication实现了IHttpAsyncHandler接口。
调用HttpApplication对象(实现了IHttpAsyncHandler接口)的BeginProcessRequest方法执行客户请求。
if (applicationInstance is IHttpAsyncHandler)
{
IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
context.AsyncAppHandler = handler2;
handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);
}
OK,回到前一步,再深入一步,进入GetNormalApplicationInstance方法之后,我们看到了HttpApplication对象是如何被创建和初始化:
private HttpApplication GetNormalApplicationInstance(HttpContext context)
{
HttpApplication state = null;
......
if (state == null)
{
state = (HttpApplication) HttpRuntime.CreateNonPublicInstance(this._theApplicationType);
using (new ApplicationImpersonationContext())
{
state.InitInternal(context, this._state, this._eventHandlerMethods);
}
}
......
}
//internal void InitInternal(HttpContext context, HttpApplicationState state,
//MethodInfo[] handlers)我们发现HttpApplication类提供了一个名为InitInternal的方法,
//调用它来完成HttpApplication实例的初始化工作,点击进入InitInternal方法内部:
internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers)
{
this._state = state;
PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES);
......
this.InitModules();
Label_006B:
if (handlers != null)
{
this.HookupEventHandlersForApplicationAndModules(handlers);
}
......
.....
if (HttpRuntime.UseIntegratedPipeline)
{
this._stepManager = new PipelineStepManager(this);
}
else
{
this._stepManager = new ApplicationStepManager(this);
} this._stepManager.BuildSteps(this._resumeStepsWaitCallback);
}
......
}
首先初始化Modules(InitModules)
private void InitModules() { HttpModuleCollection modules = RuntimeConfig.GetAppConfig().HttpModules.CreateModules(); HttpModuleCollection other = this.CreateDynamicModules(); modules.AppendCollection(other); this._moduleCollection = modules; this.InitModulesCommon(); } |
---|
接下来完成事件的绑定(19个管道事件):BuildSteps:
|
---|
BuildSteps完成HttpApplication19个管道事件的注册:
internal override void BuildSteps(WaitCallback stepCallback)
{
ArrayList steps = new ArrayList();
HttpApplication app = base._application;
bool flag = false;
UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;
flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > 0);
steps.Add(new HttpApplication.ValidateRequestExecutionStep(app));
steps.Add(new HttpApplication.ValidatePathExecutionStep(app));
if (flag)
{
steps.Add(new HttpApplication.UrlMappingsExecutionStep(app));
}
app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps);
steps.Add(new HttpApplication.MapHandlerExecutionStep(app));//---------------------->
app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);
app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);
steps.Add(app.CreateImplicitAsyncPreloadExecutionStep());
steps.Add(newHttpApplication.CallHandlerExecutionStep(app));
//---------------------->用于创建处理用户请求的对象(Handler)
private void CreateEventExecutionSteps(object eventIndex, ArrayList steps) { AsyncAppEventHandler handler = this.AsyncEvents[eventIndex]; if (handler != null) { handler.CreateExecutionSteps(this, steps); } EventHandler handler2 = (EventHandler) this.Events[eventIndex]; if (handler2 != null) { Delegate[] invocationList = handler2.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { steps.Add(new SyncEventExecutionStep(this, (EventHandler) invocationList[i])); } } } |
---|
HttpApplication对象初始化时,首先会调用InitModules方法来加载在web.config文件中配置的所有HttpModule模块。
接着HookupEventHandlersForApplicationAndModules方法被调用,这个方法完成global.asax文件中配置的HttpModule或HttpApplication事件的绑定
最后ApplicationStopManager对象的BuildSteps方法被调用,完成HttpApplication19个管道事件的注册。这个方法很重要,它将创建各种HttpApplication.IExecutionStep保存到一个数组列表:
internal override void BuildSteps(WaitCallback stepCallback)
{
.....
this._execSteps = new HttpApplication.IExecutionStep[steps.Count];
steps.CopyTo(this._execSteps);
.....
}
以便在BeginProcessRequest方法内部调用ResumeSteps方法依次执行这些对象的Execute()方法,完成各种处置。
调用BeginProcessRequest方法来实现IHttpAsyncHandler接口中定义的方法处理请求: IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { this._context = context; this._context.ApplicationInstance = this; this._stepManager.InitRequest(); this._context.Root(); HttpAsyncResult result = new HttpAsyncResult(cb, extraData); this.AsyncResult = result; if (this._context.TraceIsEnabled) { HttpRuntime.Profile.StartRequest(this._context); } this.ResumeSteps(null);//---------->依次执行管道事件 return result; } |
---|
BeginProcessRequest执行过程
void HttpApplication.IExecutionStep.Execute()
{
HttpContext context = this._application.Context;
HttpRequest request = context.Request;
if (EtwTrace.IsTraceEnabled(5, 1))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_MAPHANDLER_ENTER, context.WorkerRequest);
}
context.Handler = this._application.MapHttpHandler(context, request.RequestType, request.FilePathObject, request.PhysicalPathInternal, false);
if (EtwTrace.IsTraceEnabled(5, 1))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_MAPHANDLER_LEAVE, context.WorkerRequest);
}
}
这儿调用了一个很重要的方法MapHttpHandler:
context.Handler = this._application.MapHttpHandler(context, request.RequestType, request.FilePathObject, request.PhysicalPathInternal, false);
internal IHttpHandler MapHttpHandler(HttpContext context, string requestType, VirtualPath path, string pathTranslated, bool useAppConfig)
{
IHttpHandler handler = (context.ServerExecuteDepth == 0) ? context.RemapHandlerInstance : null;
...
IHttpHandlerFactory factory = this.GetFactory(mapping);
try
{
IHttpHandlerFactory2 factory2 = factory as IHttpHandlerFactory2;
if (factory2 != null)
{
handler = factory2.GetHandler(context, requestType, path, pathTranslated);
}
else
{
handler = factory.GetHandler(context, requestType, path.VirtualPathString, pathTranslated);
}
}
...
....
}
return handler;
}
通过实现了IHttpHandlerFactory(PageHandlerFactory 或者 SimpleHandlerFactory等)创建了HttpHandler
因为steps.Add(new HttpApplication.MapHandlerExecutionStep(app))注册了Handler,所以会在第八个事件里通过反射创建了页面请求的对象(实现了IHttpHandler接口)。
void HttpApplication.IExecutionStep.Execute() {
HttpContext context = this._application.Context;
IHttpHandler handler = context.Handler;
..... ...
IHttpAsyncHandler handler2 = (IHttpAsyncHandler) handler;
this._sync = false;
this._handler = handler2;
....
}
然后再第11个和12个事件之间,会调用了第八个事件创建的页面对象的ProcessRequest方法,具体内容详看我下一篇文章:《ASP.NET那点不为人知的事(二)》
public IPrincipal User
{
get
{
return this._principalContainer.Principal;
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries"), SecurityPermission(SecurityAction.Demand, ControlPrincipal=true)]
set
{
this.SetPrincipalNoDemand(value);
}
}
Iprincipal又有一个名为Identity,类型了System.Security.Principal.IIdentity属性
[ComVisible(true), __DynamicallyInvokable]
public interface IPrincipal
{
// Methods
[__DynamicallyInvokable]
bool IsInRole(string role);
// Properties
[__DynamicallyInvokable]
IIdentity Identity { [__DynamicallyInvokable] get; }
}
[ComVisible(true), __DynamicallyInvokable]
public interface IIdentity
{
// Properties
[__DynamicallyInvokable]
string AuthenticationType { [__DynamicallyInvokable] get; }
[__DynamicallyInvokable]
bool IsAuthenticated { [__DynamicallyInvokable] get; }
[__DynamicallyInvokable]
string Name { [__DynamicallyInvokable] get; }
}
IsAuthenticated表示当前请求用户是否已经被验证,IsAuthenticated =false,那么表示这是一个匿名用户,如果为True,那么通过IIdentity类型为string的Name属性,
这就表示当前请求的用户名。
未完,待续。