zoukankan      html  css  js  c++  java
  • 深入ASP.NET MVC 之一:IIS到路由表

    关于IIS的介绍,可以参考Introduction to IIS Architecture 。IIS到托管代码的入口点是位于System.Web dll中

    public sealed class ISAPIRuntime : MarshalByRefObject, IISAPIRuntime, IISAPIRuntime2, IRegisteredObject

    的方法

        public int ProcessRequest(IntPtr ecb, int iWRType)
            {
                IntPtr intPtr = IntPtr.Zero;
                if (iWRType == 2)
                {
                    intPtr = ecb;
                    ecb = UnsafeNativeMethods.GetEcb(intPtr);
                }
                ISAPIWorkerRequest iSAPIWorkerRequest = null;
                int result;
                try
                {
                    bool useOOP = iWRType == 1;
                    iSAPIWorkerRequest = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
                    iSAPIWorkerRequest.Initialize();
                    string appPathTranslated = iSAPIWorkerRequest.GetAppPathTranslated();
                    string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal;
                    if (appDomainAppPathInternal == null || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal))
                    {
                        HttpRuntime.ProcessRequestNoDemand(iSAPIWorkerRequest);
                        result = 0;
                    }
                    else
                    {
                             //……       
                    }
                }
                catch (Exception ex)
                {
                          //……
                }
                return result;
            }

    (注:IIS7的入口似乎是PipeLineRuntime.InitializeApplication(IntPtr appContext),过程有所不同,但是不影响后面的流程)其中ecb是一个指向httprequest的信息的指针,由IIS提供。CreateWorkerRequest根据ecb提供的信息,比如IIS的版本、模式等,创建一个ISAPIWorkerRequest对象,ISAPIWorkerReuqeuest是一个http请求的.NET封装。创建好WorkerRequest之后,调用HttpRuntime.ProcessRequestNoDemand(iSAPIWorkerRequest);开始执行请求,这个方法是会从httpRuntime对象中的一个队列中获取一个workerrequest进行处理,最终调用的是HttpRuntime类中的ProcessRequestInternal(代码有删节):

    private void ProcessRequestInternal(HttpWorkerRequest wr)
            {
                Interlocked.Increment(ref this._activeRequestCount);
                if (this._disposingHttpRuntime)
                     wr.SendStatus(503, "Server Too Busy");
                HttpContext httpContext;
                try
                {
                    httpContext = new HttpContext(wr, false);
                }
                catch
                {
                    try
                    {
                        wr.SendStatus(400, "Bad Request");
                        //…….
                    }
                    finally
                    {
                        Interlocked.Decrement(ref this._activeRequestCount);
                    }
                }
                wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, httpContext);
                HostingEnvironment.IncrementBusyCount();
                try
                {
                    httpContext.Response.InitResponseWriter();
                    IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(httpContext);
                    if (applicationInstance == null)
                    {
                        throw new HttpException(SR.GetString("Unable_create_app_object"));
                    }
                    if (applicationInstance is IHttpAsyncHandler)
                    {
                        IHttpAsyncHandler httpAsyncHandler = (IHttpAsyncHandler)applicationInstance;
                        httpContext.AsyncAppHandler = httpAsyncHandler;
                        httpAsyncHandler.BeginProcessRequest(httpContext, this._handlerCompletionCallback, httpContext);
                    }
                    else
                    {
                        applicationInstance.ProcessRequest(httpContext);
                        this.FinishRequest(httpContext.WorkerRequest, httpContext, null);
                    }
                }
                catch (Exception e)
                {
                    httpContext.Response.InitResponseWriter();
                    this.FinishRequest(wr, httpContext, e);
                }
            }

    在这段代码中,HttpRuntime可以根据当前服务器的状况回送不同的Http状态码。如果一切正常,首先根据WorkerRequest创建了HttpContext,HttpApplication根据HttpContext创建了一个IHttpHandler对象,这是一个比较复杂的过程。先看代码:

            internal static IHttpHandler GetApplicationInstance(HttpContext context)
            {
                if (HttpApplicationFactory._customApplication != null)
                {
                    return HttpApplicationFactory._customApplication;
                }
                if (context.Request.IsDebuggingRequest)
                {
                    return new HttpDebugHandler();
                }
                HttpApplicationFactory._theApplicationFactory.EnsureInited();
                HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context);
                return HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context);
            }

    customApplication应该是使用 ASP.NET State Service的时候的情况,DebugHandler应该是调试状态下的情况,不作深究,除此以外,一共有三大步骤,首先要确保调用且仅调用了一次 ApplicationFactory的Init方法,在这个方法中,主要完成了以下工作(代码有删节):

            private void Init()
            {
                 this._appFilename = HttpApplicationFactory.GetApplicationFile();
                 this.CompileApplication();
            }

    其中GetApplicationFile为:

    internal static string GetApplicationFile()
            {
                return Path.Combine(HttpRuntime.AppDomainAppPathInternal, "global.asax");
            }

    ASP.NET在这里关联上了global.asax .在CompileApplication方法中,除了回去编译ASP.NET项目中未编译的代码,还有两件重要的工作:

    private void CompileApplication()
            {
                this._theApplicationType = BuildManager.GetGlobalAsaxType();
                //Use BuilderManager to build code
                this.ReflectOnApplicationType();
            }

    首先是设置了theApplicationType,比如默认情况下,一个ASP.NET MVC的applicationType将是 MvcApplication,也就是global.asax中那个类。ReflectOnApplicationType 代码如下:

            private void ReflectOnApplicationType()
            {
                ArrayList arrayList = new ArrayList();
                MethodInfo[] methods = this._theApplicationType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
                MethodInfo[] array = methods;
                for (int i = 0; i < array.Length; i++)
                {
                    MethodInfo methodInfo = array[i];
                    if (this.ReflectOnMethodInfoIfItLooksLikeEventHandler(methodInfo))
                    {
                        arrayList.Add(methodInfo);
                    }
                }
                Type baseType = this._theApplicationType.BaseType;
                if (baseType != null && baseType != typeof(HttpApplication))
                {
                    methods = baseType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic);
                    MethodInfo[] array2 = methods;
                    for (int j = 0; j < array2.Length; j++)
                    {
                        MethodInfo methodInfo2 = array2[j];
                        if (methodInfo2.IsPrivate && this.ReflectOnMethodInfoIfItLooksLikeEventHandler(methodInfo2))
                        {
                            arrayList.Add(methodInfo2);
                        }
                    }
                }
                this._eventHandlerMethods = new MethodInfo[arrayList.Count];
                for (int k = 0; k < this._eventHandlerMethods.Length; k++)
                {
                    this._eventHandlerMethods[k] = (MethodInfo)arrayList[k];
                }
            }

    简单来说,这个方法反射了global.asax中的那个类,并且将里面的类似于EventHandler的方法放到this._eventHandlerMethods中。

    例如,ReflectOnMethodInfoIfItLooksLikeEventHandler中的代码片段:

    if (StringUtil.EqualsIgnoreCase(name, "Application_OnStart") || StringUtil.EqualsIgnoreCase(name, "Application_Start"))
                {
                    this._onStartMethod = m;
                    this._onStartParamCount = parameters.Length;
                }

    确保ApplicationFactory的Init被调用过之后,接下来,要保证Global.asax中的App_Start被调用。EnsureAppStartCalled 的核心代码就是调用了this._onStartMethod,这个方法在上面介绍Init方法中已经被初始化好。EnsureAppStartCalled做的事情虽然简单,但是其实现还是挺繁琐的,估计是为了线程安全性等考虑,不再分析其具体实现。最后,就是真正的获取一个ApplicationHandler的方法了:

            private HttpApplication GetNormalApplicationInstance(HttpContext context)
            {
                HttpApplication httpApplication = null;
                lock (this._freeList)
                {
                    if (this._numFreeAppInstances > 0)
                    {
                        httpApplication = (HttpApplication)this._freeList.Pop();
                        this._numFreeAppInstances--;
                        if (this._numFreeAppInstances < this._minFreeAppInstances)
                        {
                            this._minFreeAppInstances = this._numFreeAppInstances;
                        }
                    }
                }
                if (httpApplication == null)
                {
                    httpApplication = (HttpApplication)HttpRuntime.CreateNonPublicInstance(this._theApplicationType);
                    using (new ApplicationImpersonationContext())
                    {
                        httpApplication.InitInternal(context, this._state, this._eventHandlerMethods);
                    }
                }
                if (AppSettings.UseTaskFriendlySynchronizationContext)
                {
                    httpApplication.ApplicationInstanceConsumersCounter = new CountdownTask(1);
                    httpApplication.ApplicationInstanceConsumersCounter.Task.ContinueWith(delegate(Task _, object o)
                    {
                        HttpApplicationFactory.RecycleApplicationInstance((HttpApplication)o);
                    }, httpApplication, TaskContinuationOptions.ExecuteSynchronously);
                }
                return httpApplication;
            }

    可以看到,首先可用的HttpApplication都是缓存在一个List中的,如果没有可用的HttpApplication,则会根据theApplicationType来创建一个,核心方法是调用InitInternal方法,注意到最后一个参数是this._eventHandlerMethods,这就是global.asax中的各个EventHandler。InitInternal方法也是一个比较复杂的方法,里面对于IIS采用的是Integrated模式还是Classic模式进行分别的处理,主要完成的工作时HttpModule的初始化和处理请求过程中每个步骤触发事件处理程序的准备。先看Integrate模式下Module的初始化:

            private void InitIntegratedModules()
            {
                this._moduleCollection = this.BuildIntegratedModuleCollection(HttpApplication._moduleConfigInfo);
                this.InitModulesCommon();
            }

    第一步是根据配置的Module名字实例化Module对象,第二步代码如下:

            private void InitModulesCommon()
            {
                int count = this._moduleCollection.Count;
                for (int i = 0; i < count; i++)
                {
                    this._currentModuleCollectionKey = this._moduleCollection.GetKey(i);
                    this._moduleCollection[i].Init(this);
                }
                this._currentModuleCollectionKey = null;
                this.InitAppLevelCulture();
            }
    注意加粗的代码,它调用了IHttpModule的Init方法。这是ASP.NET挂载Module关键之处。接下来看
    public class UrlRoutingModule : IHttpModule

    这个类的Init实现。这个HttpModule是实现URL路由的关键。在.NET 4之前它是位于System.Web.Routing.dll之中的,.NET 4之后它已经被合并入System.Web.dll中了成为了Asp.NET不可分割的一部分。

            protected virtual void Init(HttpApplication application)
            {
                if (application.Context.Items[UrlRoutingModule._contextKey] != null)
                {
                    return;
                }
                application.Context.Items[UrlRoutingModule._contextKey] = UrlRoutingModule._contextKey;
                application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
            }

    最核心的代码是最后一行,它注册了 PostResolveRequestCache事件的响应程序OnApplicationPostResolveRequestCache。 响应程序的核心代码如下:

    public virtual void PostResolveRequestCache(HttpContextBase context)
            {
                RouteData routeData = this.RouteCollection.GetRouteData(context);
                if (routeData == null)
                {
                    return;
                }
                IRouteHandler routeHandler = routeData.RouteHandler;
                if (routeHandler == null)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
                }
                if (routeHandler is StopRoutingHandler)
                {
                    return;
                }
                RequestContext requestContext = new RequestContext(context, routeData);
                context.Request.RequestContext = requestContext;
                IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
                if (httpHandler == null)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[]
                    {
                        routeHandler.GetType()
                    }));
                }
                if (!(httpHandler is UrlAuthFailureHandler))
                {
                    context.RemapHandler(httpHandler);
                    return;
                }
                if (FormsAuthenticationModule.FormsAuthRequired)
                {
                    UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
                    return;
                }
                throw new HttpException(401, SR.GetString("Assess_Denied_Description3"));
            }

    在这里,代码根据RouteData获得RouteHandler,RouteHandler获得HttpHandler。这里的详情下文再分析,至此,一个HTTP请求将会通过IIS传递到路由模块了。下面再会这段代码何时会被触发。先回到InitInternal方法中第二项工作,也就是处理请求过程中每个步骤触发事件处理程序的准备。ASP.NET首先定义了一个枚举来表示处理一个request的处理周期

        public enum RequestNotification
        {
            BeginRequest = 1,
            AuthenticateRequest = 2,
            AuthorizeRequest = 4,
            ResolveRequestCache = 8,
            MapRequestHandler = 16,
            AcquireRequestState = 32,
            PreExecuteRequestHandler = 64,
            ExecuteRequestHandler = 128,
            ReleaseRequestState = 256,
            UpdateRequestCache = 512,
            LogRequest = 1024,
            EndRequest = 2048,
            SendResponse = 536870912
        }

    在InitInternal中,InitModule完成之后紧接着调用了

    private void HookupEventHandlersForApplicationAndModules(MethodInfo[] handlers)
         

    这个方法的作用是将Global.asax中的方法(事件响应)加到合适的事件上,这里的事件可以是各个HttpModule的事件。实际上就是根据命名规则去找到相应的HttpModule的事件。这部分的实现也是很有技巧性,本文不多做分析,可以参考 http://aspnetresources.com/articles/event_handlers_in_global_asax 。 再接下来,InitInternal实例化了一个StepManager,同样有Integrate和Classic的两种,下面以Integrate的PipelStepManager为例,紧接着调用了StepManager的BuildStep方法:

                internal override void BuildSteps(WaitCallback stepCallback)
                {
                    HttpApplication application = this._application;
                    HttpApplication.IExecutionStep step = new HttpApplication.MaterializeHandlerExecutionStep(application);
                    application.AddEventMapping("ManagedPipelineHandler", RequestNotification.MapRequestHandler, false, step);
                    application.AddEventMapping("ManagedPipelineHandler", RequestNotification.ExecuteRequestHandler, false, application.CreateImplicitAsyncPreloadExecutionStep());
                    HttpApplication.IExecutionStep step2 = new HttpApplication.CallHandlerExecutionStep(application);
                    application.AddEventMapping("ManagedPipelineHandler", RequestNotification.ExecuteRequestHandler, false, step2);
                    HttpApplication.IExecutionStep step3 = new HttpApplication.TransitionToWebSocketsExecutionStep(application);
                    application.AddEventMapping("ManagedPipelineHandler", RequestNotification.EndRequest, true, step3);
                    HttpApplication.IExecutionStep step4 = new HttpApplication.CallFilterExecutionStep(application);
                    application.AddEventMapping("AspNetFilterModule", RequestNotification.UpdateRequestCache, false, step4);
                    application.AddEventMapping("AspNetFilterModule", RequestNotification.LogRequest, false, step4);
                    this._resumeStepsWaitCallback = stepCallback;
                }

    这里面实例化了很多具体的IExecutionStep对象,并且和RequestNotification关联起来。这些step将是完成一个request的必要步骤。AddEventMapping的核心代码如下:

                PipelineModuleStepContainer moduleContainer = this.GetModuleContainer(moduleName);
                if (moduleContainer != null)
                {
                    moduleContainer.AddEvent(requestNotification, isPostNotification, step);
                }

    moduleContainer 中有一个IExecutionStep列表,里面的step是按照requestNotification的顺序排列的,这点非常重要。至此,InitInternal的工作基本完成了。HttpApplication的一个实例也已经初始化完毕,直接跳回至ProccessRequestInternal方法,接下来就是调用BeginProcessRequest开始真正的处理了。这个方法的核心是调用 StepManager的ResumeSteps方法。更具体的,对于使用Integrated模式的ASP.NET的项目来说,是调用了PipelineStepManager的ResumeSteps方法。这个方法也很复杂,但是核心的代码就是两行:

         HttpApplication.IExecutionStep nextEvent = this._application.CurrentModuleContainer.GetNextEvent(context.CurrentNotification, context.IsPostNotification, context.CurrentModuleEventIndex);
         error = this._application.ExecuteStep(nextEvent, ref flag3);

    也就是从PipelineModuleStepContainer中取出准备好的step逐个执行。本文不再分析每个step的具体内容,有了以上的准备,接下来看本文的主题,routing module是什么时候被执行的。 回到上面routing module的Init方法中注册事件的方法,其内部实现是:

            public event EventHandler PostResolveRequestCache
            {
                add
                {
                    this.AddSyncEventHookup(HttpApplication.EventPostResolveRequestCache, value, RequestNotification.ResolveRequestCache, true);
                }
                remove
                {
                    this.RemoveSyncEventHookup(HttpApplication.EventPostResolveRequestCache, value, RequestNotification.ResolveRequestCache, true);
                }
            }

    AddSyncEventHookup的核心代码如下:

                    PipelineModuleStepContainer moduleContainer = this.GetModuleContainer(this.CurrentModuleCollectionKey);
                    if (moduleContainer != null)
                    {
                        HttpApplication.SyncEventExecutionStep step = new HttpApplication.SyncEventExecutionStep(this, (EventHandler)handler);
                        moduleContainer.AddEvent(notification, isPostNotification, step);
                    }

    在这里,他添加了一个SyncEventExecutionStep到moduleContainer中,因此,在执行到HttpApplication.EventPostResolveRequestCache的step的时候,SyncEventExecutionStep的Execute方法将被执行,这个方法的核心代码是:

    this._handler(this._application, this._application.AppEvent);

    这里的_handler,将会是new EventHandler(this.OnApplicationPostResolveRequestCache); _application是HttpApplication实例。

    因此,routing module的代码就被执行了。

    小结:本文大致分析了System.Web中的源代码,以asp.net中一个request的生命周期是如何的,介绍了routing module是怎样挂载到这个处理流程中的。Routing module只是一个普通的http module,其他的http module原理类似。下文将重点介绍routing module是如何工作的。

  • 相关阅读:
    IIS7中的几种身份鉴别方式(一)Basic身份验证
    IIS7中的几种身份鉴别方式(二)集成身份验证
    java集合
    SharePoint 2010中welcome page的设置细节
    SharePoint中使用Linq出现未将对象引用到实例化的解决方法
    SharePoint 2010中关于An error was encountered while retrieving the user profile的处理方式记录
    The Need for an Architectural Body of Knowledge
    The Softer Side of the Architect
    Event Receivers 学习小结
    使用SmtpClient发送带图片的邮件的代码实现
  • 原文地址:https://www.cnblogs.com/yinzixin/p/2745713.html
Copyright © 2011-2022 走看看