AppDomainFactory.cs
1. public Object Create(String appId, String appPath)
public Object Create(String appId, String appPath) { try { // // Fill app a Dictionary with 'binding rules' -- name value string pairs // for app domain creation // // if (appPath[0] == '.') { System.IO.FileInfo file = new System.IO.FileInfo(appPath); appPath = file.FullName; } if (!StringUtil.StringEndsWith(appPath, '\')) { appPath = appPath + "\"; } // Create new app domain via App Manager #if FEATURE_PAL // FEATURE_PAL does not enable IIS-based hosting features throw new NotImplementedException("ROTORTODO"); #else // FEATURE_PAL
//创建应用程序域 ISAPIApplicationHost appHost = new ISAPIApplicationHost(appId, appPath, false /*validatePhysicalPath*/);
//创建ISAPIRuntime ISAPIRuntime isapiRuntime = (ISAPIRuntime)_appManager.CreateObjectInternal(appId, typeof(ISAPIRuntime), appHost, false /*failIfExists*/, null /*hostingParameters*/); isapiRuntime.StartProcessing(); return new ObjectHandle(isapiRuntime); #endif // FEATURE_PAL } catch (Exception e) { Debug.Trace("internal", "AppDomainFactory::Create failed with " + e.GetType().FullName + ": " + e.Message + " " + e.StackTrace); throw; } }
2.appManager.Create._appManager.CreateObjectInternal
internal IRegisteredObject CreateObjectInternal( String appId, Type type, IApplicationHost appHost, bool failIfExists, HostingEnvironmentParameters hostingParameters) { // check that type is as IRegisteredObject if (!typeof(IRegisteredObject).IsAssignableFrom(type)) throw new ArgumentException(SR.GetString(SR.Not_IRegisteredObject, type.FullName), "type"); // get hosting environment 创建宿主环境 HostingEnvironment env = GetAppDomainWithHostingEnvironment(appId, appHost, hostingParameters); // create the managed object in the worker app domain // When marshaling Type, the AppDomain must have FileIoPermission to the assembly, which is not // always the case, so we marshal the assembly qualified name instead ObjectHandle h = env.CreateWellKnownObjectInstance(type.AssemblyQualifiedName, failIfExists); return (h != null) ? h.Unwrap() as IRegisteredObject : null; }
ISAPIRuntime
public int ProcessRequest(IntPtr ecb, int iWRType) { IntPtr pHttpCompletion = IntPtr.Zero; if (iWRType == WORKER_REQUEST_TYPE_IN_PROC_VERSION_2) { pHttpCompletion = ecb; ecb = UnsafeNativeMethods.GetEcb(pHttpCompletion); } ISAPIWorkerRequest wr = null; try { bool useOOP = (iWRType == WORKER_REQUEST_TYPE_OOP); wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP); wr.Initialize(); // check if app path matches (need to restart app domain?) String wrPath = wr.GetAppPathTranslated(); String adPath = HttpRuntime.AppDomainAppPathInternal; if (adPath == null || StringUtil.EqualsIgnoreCase(wrPath, adPath)) { HttpRuntime.ProcessRequestNoDemand(wr); return 0; } else { // need to restart app domain HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString(SR.Hosting_Phys_Path_Changed, adPath, wrPath)); return 1; } } catch(Exception e) { try { WebBaseEvent.RaiseRuntimeError(e, this); } catch {} // Have we called HSE_REQ_DONE_WITH_SESSION? If so, don't re-throw. if (wr != null && wr.Ecb == IntPtr.Zero) { if (pHttpCompletion != IntPtr.Zero) { UnsafeNativeMethods.SetDoneWithSessionCalled(pHttpCompletion); } // if this is a thread abort exception, cancel the abort if (e is ThreadAbortException) { Thread.ResetAbort(); } // IMPORTANT: if this thread is being aborted because of an AppDomain.Unload, // the CLR will still throw an AppDomainUnloadedException. The native caller // must special case COR_E_APPDOMAINUNLOADED(0x80131014) and not // call HSE_REQ_DONE_WITH_SESSION more than once. return 0; } // re-throw if we have not called HSE_REQ_DONE_WITH_SESSION throw; } }
HttpRuntime
1.ProcessRequestNow().ProcessRequestInternal()
1.创建HttpContext
2.向HttpApplicationFactory类的一个实例申请一个HttpApplication
3.调用HttpApplication的InitalModule
private void ProcessRequestInternal(HttpWorkerRequest wr) { // Count active requests Interlocked.Increment(ref _activeRequestCount); if (_disposingHttpRuntime) { // Dev11 333176: An appdomain is unloaded before all requests are served, resulting in System.AppDomainUnloadedException during isapi completion callback // // HttpRuntim.Dispose could have already finished on a different thread when we had no active requests // In this case we are about to start or already started unloading the appdomain so we will reject the request the safest way possible try { wr.SendStatus(503, "Server Too Busy"); wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8"); byte[] body = Encoding.ASCII.GetBytes("<html><body>Server Too Busy</body></html>"); wr.SendResponseFromMemory(body, body.Length); // this will flush synchronously because of HttpRuntime.ShutdownInProgress wr.FlushResponse(true); wr.EndOfRequest(); } finally { Interlocked.Decrement(ref _activeRequestCount); } return; } // Construct the Context on HttpWorkerRequest, hook everything together HttpContext context; try { context = new HttpContext(wr, false /* initResponseWriter */); } catch { try { // If we fail to create the context for any reason, send back a 400 to make sure // the request is correctly closed (relates to VSUQFE3962) wr.SendStatus(400, "Bad Request"); wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8"); byte[] body = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>"); wr.SendResponseFromMemory(body, body.Length); wr.FlushResponse(true); wr.EndOfRequest(); return; } finally { Interlocked.Decrement(ref _activeRequestCount); } } wr.SetEndOfSendNotification(_asyncEndOfSendCallback, context); HostingEnvironment.IncrementBusyCount(); try { // First request initialization try { EnsureFirstRequestInit(context); } catch { // If we are handling a DEBUG request, ignore the FirstRequestInit exception. // This allows the HttpDebugHandler to execute, and lets the debugger attach to // the process (VSWhidbey 358135) if (!context.Request.IsDebuggingRequest) { throw; } } // Init response writer (after we have config in first request init) // no need for impersonation as it is handled in config system context.Response.InitResponseWriter(); // Get application instance IHttpHandler app = HttpApplicationFactory.GetApplicationInstance(context); if (app == null) throw new HttpException(SR.GetString(SR.Unable_create_app_object)); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, app.GetType().FullName, "Start"); if (app is IHttpAsyncHandler) { // asynchronous handler IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app; context.AsyncAppHandler = asyncHandler; asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context); } else { // synchronous handler app.ProcessRequest(context); FinishRequest(context.WorkerRequest, context, null); } } catch (Exception e) { context.Response.InitResponseWriter(); FinishRequest(wr, context, e); } }
IHttpHandler app = HttpApplicationFactory.GetApplicationInstance(context);
internal static IHttpHandler GetApplicationInstance(HttpContext context) { if (_customApplication != null) return _customApplication; // Check to see if it's a debug auto-attach request if (context.Request.IsDebuggingRequest) return new HttpDebugHandler(); _theApplicationFactory.EnsureInited(); _theApplicationFactory.EnsureAppStartCalled(context); return _theApplicationFactory.GetNormalApplicationInstance(context); } private HttpApplication GetNormalApplicationInstance(HttpContext context) { HttpApplication app = null; lock (_freeList) { if (_numFreeAppInstances > 0) { app = (HttpApplication)_freeList.Pop(); _numFreeAppInstances--; if (_numFreeAppInstances < _minFreeAppInstances) { _minFreeAppInstances = _numFreeAppInstances; } } } if (app == null) { // If ran out of instances, create a new one app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType); using (new ApplicationImpersonationContext()) { app.InitInternal(context, _state, _eventHandlerMethods); } } if (AppSettings.UseTaskFriendlySynchronizationContext) { // When this HttpApplication instance is no longer in use, recycle it. app.ApplicationInstanceConsumersCounter = new CountdownTask(1); // representing required call to HttpApplication.ReleaseAppInstance app.ApplicationInstanceConsumersCounter.Task.ContinueWith((_, o) => RecycleApplicationInstance((HttpApplication)o), app, TaskContinuationOptions.ExecuteSynchronously); } return app; }
HttpApplication
internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) { Debug.Assert(context != null, "context != null"); // Remember state _state = state; PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES); try { try { // Remember context for config lookups _initContext = context; _initContext.ApplicationInstance = this; // Set config path to be application path for the application initialization context.ConfigurationPath = context.Request.ApplicationPathObject; // keep HttpContext.Current working while running user code using (new DisposableHttpContextWrapper(context)) { // Build module list from config if (HttpRuntime.UseIntegratedPipeline) { Debug.Assert(_moduleConfigInfo != null, "_moduleConfigInfo != null"); Debug.Assert(_moduleConfigInfo.Count >= 0, "_moduleConfigInfo.Count >= 0"); try { context.HideRequestResponse = true; _hideRequestResponse = true; InitIntegratedModules(); } finally { context.HideRequestResponse = false; _hideRequestResponse = false; } } else { InitModules(); // this is used exclusively for integrated mode Debug.Assert(null == _moduleContainers, "null == _moduleContainers"); } // Hookup event handlers via reflection if (handlers != null) HookupEventHandlersForApplicationAndModules(handlers); // Initialization of the derived class _context = context; if (HttpRuntime.UseIntegratedPipeline && _context != null) { _context.HideRequestResponse = true; } _hideRequestResponse = true; try { Init(); } catch (Exception e) { RecordError(e); } } if (HttpRuntime.UseIntegratedPipeline && _context != null) { _context.HideRequestResponse = false; } _hideRequestResponse = false; _context = null; _resumeStepsWaitCallback= new WaitCallback(this.ResumeStepsWaitCallback); // Construct the execution steps array if (HttpRuntime.UseIntegratedPipeline) { _stepManager = new PipelineStepManager(this); } else { _stepManager = new ApplicationStepManager(this); } _stepManager.BuildSteps(_resumeStepsWaitCallback); } finally { _initInternalCompleted = true; // Reset config path context.ConfigurationPath = null; // don't hold on to the context _initContext.ApplicationInstance = null; _initContext = null; } } catch { // Protect against exception filters throw; } }
private void HookupEventHandlersForApplicationAndModules(MethodInfo[] handlers) { _currentModuleCollectionKey = HttpApplicationFactory.applicationFileName; if(null == _pipelineEventMasks) { Dictionary<string, RequestNotification> dict = new Dictionary<string, RequestNotification>(); BuildEventMaskDictionary(dict); if(null == _pipelineEventMasks) { _pipelineEventMasks = dict; } } for (int i = 0; i < handlers.Length; i++) { MethodInfo appMethod = handlers[i]; String appMethodName = appMethod.Name; int namePosIndex = appMethodName.IndexOf('_'); String targetName = appMethodName.Substring(0, namePosIndex); // Find target for method Object target = null; if (StringUtil.EqualsIgnoreCase(targetName, "Application")) target = this; else if (_moduleCollection != null) target = _moduleCollection[targetName]; if (target == null) continue; // Find event on the module type Type targetType = target.GetType(); EventDescriptorCollection events = TypeDescriptor.GetEvents(targetType); string eventName = appMethodName.Substring(namePosIndex+1); EventDescriptor foundEvent = events.Find(eventName, true); if (foundEvent == null && StringUtil.EqualsIgnoreCase(eventName.Substring(0, 2), "on")) { eventName = eventName.Substring(2); foundEvent = events.Find(eventName, true); } MethodInfo addMethod = null; if (foundEvent != null) { EventInfo reflectionEvent = targetType.GetEvent(foundEvent.Name); Debug.Assert(reflectionEvent != null); if (reflectionEvent != null) { addMethod = reflectionEvent.GetAddMethod(); } } if (addMethod == null) continue; ParameterInfo[] addMethodParams = addMethod.GetParameters(); if (addMethodParams.Length != 1) continue; // Create the delegate from app method to pass to AddXXX(handler) method Delegate handlerDelegate = null; ParameterInfo[] appMethodParams = appMethod.GetParameters(); if (appMethodParams.Length == 0) { // If the app method doesn't have arguments -- // -- hookup via intermidiate handler // only can do it for EventHandler, not strongly typed if (addMethodParams[0].ParameterType != typeof(System.EventHandler)) continue; ArglessEventHandlerProxy proxy = new ArglessEventHandlerProxy(this, appMethod); handlerDelegate = proxy.Handler; } else { // Hookup directly to the app methods hoping all types match try { handlerDelegate = Delegate.CreateDelegate(addMethodParams[0].ParameterType, this, appMethodName); } catch { // some type mismatch continue; } } // Call the AddXXX() to hook up the delegate try { addMethod.Invoke(target, new Object[1]{handlerDelegate}); } catch { if (HttpRuntime.UseIntegratedPipeline) { throw; } } if (eventName != null) { if (_pipelineEventMasks.ContainsKey(eventName)) { if (!StringUtil.StringStartsWith(eventName, "Post")) { _appRequestNotifications |= _pipelineEventMasks[eventName]; } else { _appPostNotifications |= _pipelineEventMasks[eventName]; } } } } }
private void RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) { RequestNotification requestNotifications; RequestNotification postRequestNotifications; // register an implicit filter module RegisterIntegratedEvent(appContext, IMPLICIT_FILTER_MODULE, RequestNotification.UpdateRequestCache| RequestNotification.LogRequest /*requestNotifications*/, 0 /*postRequestNotifications*/, String.Empty /*type*/, String.Empty /*precondition*/, true /*useHighPriority*/); // integrated pipeline will always use serverModules instead of <httpModules> _moduleCollection = GetModuleCollection(appContext); if (handlers != null) { HookupEventHandlersForApplicationAndModules(handlers); } // 1643363: Breaking Change: ASP.Net v2.0: Application_OnStart is called after Module.Init (Integarted mode) HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(context, this); // Call Init on HttpApplication derived class ("global.asax") // and process event subscriptions before processing other modules. // Doing this now prevents clearing any events that may // have been added to event handlers during instantiation of this instance. // NOTE: If "global.asax" has a constructor which hooks up event handlers, // then they were added to the event handler lists but have not been registered with IIS yet, // so we MUST call ProcessEventSubscriptions on it first, before the other modules. _currentModuleCollectionKey = HttpApplicationFactory.applicationFileName; try { _hideRequestResponse = true; context.HideRequestResponse = true; _context = context; Init(); } catch (Exception e) { RecordError(e); Exception error = context.Error; if (error != null) { throw error; } } finally { _context = null; context.HideRequestResponse = false; _hideRequestResponse = false; } ProcessEventSubscriptions(out requestNotifications, out postRequestNotifications); // Save the notification subscriptions so we can register them with IIS later, after // we call HookupEventHandlersForApplicationAndModules and process global.asax event handlers. _appRequestNotifications |= requestNotifications; _appPostNotifications |= postRequestNotifications; for (int i = 0; i < _moduleCollection.Count; i++) { _currentModuleCollectionKey = _moduleCollection.GetKey(i); IHttpModule httpModule = _moduleCollection.Get(i); ModuleConfigurationInfo moduleInfo = _moduleConfigInfo[i]; #if DBG Debug.Trace("PipelineRuntime", "RegisterEventSubscriptionsWithIIS: name=" + CurrentModuleCollectionKey + ", type=" + httpModule.GetType().FullName + " "); // make sure collections are in sync Debug.Assert(moduleInfo.Name == _currentModuleCollectionKey, "moduleInfo.Name == _currentModuleCollectionKey"); #endif httpModule.Init(this); ProcessEventSubscriptions(out requestNotifications, out postRequestNotifications); // are any events wired up? if (requestNotifications != 0 || postRequestNotifications != 0) { RegisterIntegratedEvent(appContext, moduleInfo.Name, requestNotifications, postRequestNotifications, moduleInfo.Type, moduleInfo.Precondition, false /*useHighPriority*/); } } // WOS 1728067: RewritePath does not remap the handler when rewriting from a non-ASP.NET request // register a default implicit handler RegisterIntegratedEvent(appContext, IMPLICIT_HANDLER, RequestNotification.ExecuteRequestHandler | RequestNotification.MapRequestHandler /*requestNotifications*/, RequestNotification.EndRequest /*postRequestNotifications*/, String.Empty /*type*/, String.Empty /*precondition*/, false /*useHighPriority*/); }
UrlRoutingModule
protected virtual void Init(HttpApplication application) { ////////////////////////////////////////////////////////////////// // Check if this module has been already addded if (application.Context.Items[_contextKey] != null) { return; // already added to the pipeline } application.Context.Items[_contextKey] = _contextKey; // Ideally we would use the MapRequestHandler event. However, MapRequestHandler is not available // in II6 or IIS7 ISAPI Mode. Instead, we use PostResolveRequestCache, which is the event immediately // before MapRequestHandler. This allows use to use one common codepath for all versions of IIS. application.PostResolveRequestCache += OnApplicationPostResolveRequestCache; }
public virtual void PostResolveRequestCache(HttpContextBase context) { // Match the incoming URL against the route table RouteData routeData = RouteCollection.GetRouteData(context); // Do nothing if no route found if (routeData == null) { return; } // If a route was found, get an IHttpHandler from the route's RouteHandler IRouteHandler routeHandler = routeData.RouteHandler; if (routeHandler == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, SR.GetString(SR.UrlRoutingModule_NoRouteHandler))); } // This is a special IRouteHandler that tells the routing module to stop processing // routes and to let the fallback handler handle the request. if (routeHandler is StopRoutingHandler) { return; } RequestContext requestContext = new RequestContext(context, routeData); // Dev10 766875 Adding RouteData to HttpContext context.Request.RequestContext = requestContext; IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);//创建MVCHandler 并将RequestContext存入
if (httpHandler == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentUICulture, SR.GetString(SR.UrlRoutingModule_NoHttpHandler), routeHandler.GetType())); } if (httpHandler is UrlAuthFailureHandler) { if (FormsAuthenticationModule.FormsAuthRequired) { UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this); return; } else { throw new HttpException(401, SR.GetString(SR.Assess_Denied_Description3)); } } // Remap IIS7 to our handler context.RemapHandler(httpHandler);//将MVCHandler存入HttpContext对象 }
RouteData routeData = RouteCollection.GetRouteData(context)
RouteCollection
public RouteData GetRouteData(HttpContextBase httpContext) { if (httpContext == null) { throw new ArgumentNullException("httpContext"); } if (httpContext.Request == null) { throw new ArgumentException(SR.GetString(SR.RouteTable_ContextMissingRequest), "httpContext"); } // Optimize performance when the route collection is empty. The main improvement is that we avoid taking // a read lock when the collection is empty. Without this check, the UrlRoutingModule causes a 25%-50% // regression in HelloWorld RPS due to lock contention. The UrlRoutingModule is now in the root web.config, // so we need to ensure the module is performant, especially when you are not using routing. // This check does introduce a slight if (Count == 0) { return null; } bool isRouteToExistingFile = false; bool doneRouteCheck = false; // We only want to do the route check once if (!RouteExistingFiles) { isRouteToExistingFile = IsRouteToExistingFile(httpContext); doneRouteCheck = true; if (isRouteToExistingFile) { // If we're not routing existing files and the file exists, we stop processing routes return null; } } // Go through all the configured routes and find the first one that returns a match using (GetReadLock()) { foreach (RouteBase route in this) { RouteData routeData = route.GetRouteData(httpContext); if (routeData != null) { // If we're not routing existing files on this route and the file exists, we also stop processing routes if (!route.RouteExistingFiles) { if (!doneRouteCheck) { isRouteToExistingFile = IsRouteToExistingFile(httpContext); doneRouteCheck = true; } if (isRouteToExistingFile) { return null; } } return routeData; } } } return null; }
RouteData routeData = route.GetRouteData(httpContext);
Route
public override RouteData GetRouteData(HttpContextBase httpContext) { // Parse incoming URL (we trim off the first two chars since they're always "~/") string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo; RouteValueDictionary values = _parsedRoute.Match(requestPath, Defaults); if (values == null) { // If we got back a null value set, that means the URL did not match return null; } RouteData routeData = new RouteData(this, RouteHandler); // Validate the values if (!ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest)) { return null; } // Copy the matched values foreach (var value in values) { routeData.Values.Add(value.Key, value.Value); } // Copy the DataTokens from the Route to the RouteData if (DataTokens != null) { foreach (var prop in DataTokens) { routeData.DataTokens[prop.Key] = prop.Value; } } return routeData; }
Controller
protected override void ExecuteCore() { this.PossiblyLoadTempData(); try { string requiredString = this.RouteData.GetRequiredString("action"); if (this.ActionInvoker.InvokeAction(this.ControllerContext, requiredString)) return; this.HandleUnknownAction(requiredString); } finally { this.PossiblySaveTempData(); } }
ControllerActionInvoker
/// <summary> /// 使用指定的控制器上下文来调用指定操作。 /// </summary> /// /// <returns> /// 执行操作的结果。 /// </returns> /// <param name="controllerContext">控制器上下文。</param><param name="actionName">要调用的操作的名称。</param><exception cref="T:System.ArgumentNullException"><paramref name="controllerContext"/> 参数为 null。</exception><exception cref="T:System.ArgumentException"><paramref name="actionName"/> 参数为 null 或为空。</exception><exception cref="T:System.Threading.ThreadAbortException">线程在此操作的调用期间已中止。</exception><exception cref="T:System.Exception">在此操作的调用期间出现未指定的错误。</exception> public virtual bool InvokeAction(ControllerContext controllerContext, string actionName) { if (controllerContext == null) throw new ArgumentNullException("controllerContext"); if (string.IsNullOrEmpty(actionName)) throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName"); ControllerDescriptor controllerDescriptor = this.GetControllerDescriptor(controllerContext); ActionDescriptor action = this.FindAction(controllerContext, controllerDescriptor, actionName); if (action == null) return false; FilterInfo filters = this.GetFilters(controllerContext, action); try { AuthorizationContext authorizationContext = this.InvokeAuthorizationFilters(controllerContext, filters.AuthorizationFilters, action); if (authorizationContext.Result != null) { this.InvokeActionResult(controllerContext, authorizationContext.Result); } else { if (controllerContext.Controller.ValidateRequest) ControllerActionInvoker.ValidateRequest(controllerContext); IDictionary<string, object> parameterValues = this.GetParameterValues(controllerContext, action); ActionExecutedContext actionExecutedContext = this.InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, action, parameterValues); this.InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, actionExecutedContext.Result); } } catch (ThreadAbortException ex) { throw; } catch (Exception ex) { ExceptionContext exceptionContext = this.InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, ex); if (!exceptionContext.ExceptionHandled) throw; else this.InvokeActionResult(controllerContext, exceptionContext.Result); } return true; }
/// <summary> /// 创建一个实现 IHttpHandler 接口的对象并向该对象传递请求上下文。 /// </summary> public class MvcRouteHandler : IRouteHandler { private IControllerFactory _controllerFactory; /// <summary> /// 初始化 <see cref="T:System.Web.Mvc.MvcRouteHandler"/> 类的新实例。 /// </summary> public MvcRouteHandler() { } /// <summary> /// 使用指定的工厂控制器对象初始化 <see cref="T:System.Web.Mvc.MvcRouteHandler"/> 类的新实例。 /// </summary> /// <param name="controllerFactory">控制器工厂。</param> public MvcRouteHandler(IControllerFactory controllerFactory) { this._controllerFactory = controllerFactory; } /// <summary> /// 使用指定的 HTTP 上下文返回 HTTP 处理程序。 /// </summary> /// /// <returns> /// HTTP 处理程序。 /// </returns> /// <param name="requestContext">请求上下文。</param> protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) { requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext)); return (IHttpHandler) new MvcHandler(requestContext); }