设为首页 收藏本站
查看: 921|回复: 0

[经验分享] 深入ASP.NET MVC 之一:IIS到路由表

[复制链接]

尚未签到

发表于 2015-8-12 10:07:19 | 显示全部楼层 |阅读模式
  关于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;
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, &quot;Application_OnStart&quot;) || StringUtil.EqualsIgnoreCase(name, &quot;Application_Start&quot;))
{
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.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(&quot;UrlRoutingModule_NoRouteHandler&quot;), 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(&quot;UrlRoutingModule_NoHttpHandler&quot;), 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(&quot;Assess_Denied_Description3&quot;));
}
  在这里,代码根据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(&quot;ManagedPipelineHandler&quot;, RequestNotification.MapRequestHandler, false, step);
application.AddEventMapping(&quot;ManagedPipelineHandler&quot;, RequestNotification.ExecuteRequestHandler, false, application.CreateImplicitAsyncPreloadExecutionStep());
HttpApplication.IExecutionStep step2 = new HttpApplication.CallHandlerExecutionStep(application);
application.AddEventMapping(&quot;ManagedPipelineHandler&quot;, RequestNotification.ExecuteRequestHandler, false, step2);
HttpApplication.IExecutionStep step3 = new HttpApplication.TransitionToWebSocketsExecutionStep(application);
application.AddEventMapping(&quot;ManagedPipelineHandler&quot;, RequestNotification.EndRequest, true, step3);
HttpApplication.IExecutionStep step4 = new HttpApplication.CallFilterExecutionStep(application);
application.AddEventMapping(&quot;AspNetFilterModule&quot;, RequestNotification.UpdateRequestCache, false, step4);
application.AddEventMapping(&quot;AspNetFilterModule&quot;, 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是如何工作的。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-97807-1-1.html 上篇帖子: IIS 8:IIS 入门 下篇帖子: 利用windbg查找dictionary导致IIS占CPU100%案例分析(一)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表