上一篇, 出現了一個至關重要的類:MvcHandler, 接下來就來看一下MvcHandler吧. 先不看具體方法, 先看一下類裡面的情況. 從上面看, 有兩種執行方式, 一種是同步的, 一種是非同步的. 那預設情況下, 其實會走非同步的方式. 但是這裡呢, 我想用同步的方式去分析, 其實過程原理都是一 ...
上一篇, 出現了一個至關重要的類:MvcHandler, 接下來就來看一下MvcHandler吧. 先不看具體方法, 先看一下類裡面的情況.
//這裡實現了兩個重要的介面, 非同步處理和同步處理的介面
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState { // Fields private ControllerBuilder _controllerBuilder; private static readonly object _processRequestTag; internal static readonly string MvcVersion; public static readonly string MvcVersionHeaderName; // Methods static MvcHandler(); public MvcHandler(RequestContext requestContext); protected internal virtual void AddVersionHeader(HttpContextBase httpContext); protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state); protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state); protected internal virtual void EndProcessRequest(IAsyncResult asyncResult); private static string GetMvcVersionString(); protected virtual void ProcessRequest(HttpContext httpContext); protected internal virtual void ProcessRequest(HttpContextBase httpContext); private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory); private void RemoveOptionalRoutingParameters(); IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData); void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result); void IHttpHandler.ProcessRequest(HttpContext httpContext); // Properties internal ControllerBuilder ControllerBuilder { get; set; } public static bool DisableMvcResponseHeader { get; set; } protected virtual bool IsReusable { get; } public RequestContext RequestContext { get; private set; } bool IHttpHandler.IsReusable { get; } }
從上面看, 有兩種執行方式, 一種是同步的, 一種是非同步的. 那預設情況下, 其實會走非同步的方式. 但是這裡呢, 我想用同步的方式去分析, 其實過程原理都是一樣的, 只是方式不同.
一、解析
註意到這個類, 實現了三個介面, 那第三個介面是幹啥的呢? 先看一下這個介面的內容.
public interface IRequiresSessionState { }
點進去, 發現這個介面沒有任何方法, 那麼他是乾什麼的呢?
其實他是一種標誌, 或者叫標記, 表示這個類有對Session的訪問許可權, 包括讀寫. 如果你想自定義Http處理程式, 又想操作Session的話, 記得實現這個介面.
接下來, 回歸正題了. 來看一下這裡的 PR 方法.
protected virtual void ProcessRequest(HttpContext httpContext) { HttpContextBase base2 = new HttpContextWrapper(httpContext); this.ProcessRequest(base2); } protected internal virtual void ProcessRequest(HttpContextBase httpContext) { IController controller; IControllerFactory factory; this.ProcessRequestInit(httpContext, out controller, out factory); try { controller.Execute(this.RequestContext); } finally { factory.ReleaseController(controller); }
}
這裡也就只有三個方法了, 我們一個一個分析.
1. ProcessRequestInit()
//MvcHandler
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) { HttpContext current = HttpContext.Current; if ((current != null) && (ValidationUtility.IsValidationEnabled(current) == true)) { ValidationUtility.EnableDynamicValidation(current); } this.AddVersionHeader(httpContext); this.RemoveOptionalRoutingParameters();
//從路由中獲取控制器的名稱 string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
//獲取控制器工廠 factory = this.ControllerBuilder.GetControllerFactory();
//利用控制器工廠創建控制器類 controller = factory.CreateController(this.RequestContext, requiredString); if (controller == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
MvcResources.ControllerBuilder_FactoryReturnedNull, new object[] { factory.GetType(), requiredString })); } }
1.1 從路由中獲取控制器名稱 - GetRequiredString
public string GetRequiredString(string valueName) { object obj2; if (this.Values.TryGetValue(valueName, out obj2)) { string str = obj2 as string; if (!string.IsNullOrEmpty(str)) { return str; } } throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture,
SR.GetString("RouteData_RequiredValue"), new object[] { valueName })); }
1.2 獲取創建工廠 - GetControllerFactory
public IControllerFactory GetControllerFactory() { return this._serviceResolver.Current; }
1.3 創建控制器類 - CreateController
//DefaultControllerFactory類
public virtual IController CreateController(RequestContext requestContext, string controllerName) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (string.IsNullOrEmpty(controllerName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName"); } Type controllerType = this.GetControllerType(requestContext, controllerName); return this.GetControllerInstance(requestContext, controllerType); }
1.3.1 GetControllerType()
protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) { object obj2; Type type; if (string.IsNullOrEmpty(controllerName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName"); } if ((requestContext != null) && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out obj2)) { IEnumerable<string> source = obj2 as IEnumerable<string>; if ((source != null) && source.Any<string>()) { HashSet<string> namespaces = new HashSet<string>(source, StringComparer.OrdinalIgnoreCase); type = this.GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, namespaces); if (type == null) { bool flag = false; if (!flag.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"])) { goto Label_0092; } } return type; } } Label_0092: if (this.ControllerBuilder.DefaultNamespaces.Count > 0) { HashSet<string> set2 = new HashSet<string>(this.ControllerBuilder.DefaultNamespaces,
StringComparer.OrdinalIgnoreCase); type = this.GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, set2); if (type != null) { return type; } } return this.GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null); }
這裡會從路由中, 獲取控制器所在的命名空間. 不管由這個限制還是沒有這個限制, 都是能正常創建控制器的. 先看這裡.
private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces) {
//MVC-ControllerTypeCache.xml文件中獲取Controller的緩存 this.ControllerTypeCache.EnsureInitialized(this.BuildManager);
//從之前獲取的緩存中, 來獲取控制器的類型 ICollection<Type> controllerTypes = this.ControllerTypeCache.GetControllerTypes(controllerName, namespaces); switch (controllerTypes.Count) { case 0: return null; case 1: return controllerTypes.First<Type>(); } throw CreateAmbiguousControllerException(route, controllerName, controllerTypes); }
1.3.2 GetControllerInstance()
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType == null) { throw new HttpException(0x194,
string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_NoControllerFound,
new object[] { requestContext.HttpContext.Request.Path })); } if (!typeof(IController).IsAssignableFrom(controllerType)) { throw new ArgumentException(
string.Format(CultureInfo.CurrentCulture,
MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,
new object[] { controllerType }), "controllerType"); } return this.ControllerActivator.Create(requestContext, controllerType); }
來看一下這裡的Create方法, 看一下具體是怎麼創建的.
public IController Create(RequestContext requestContext, Type controllerType) { IController controller; try { controller = (IController) (this._resolverThunk().GetService(controllerType) ??
Activator.CreateInstance(controllerType)); } catch (Exception exception) { throw new InvalidOperationException(
string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController,
new object[] { controllerType }), exception); } return controller; }
這裡有一個擴展點, 像之前篇幅提到的Autofac Mvc部分, 在這裡, 反饋的GetService(controllerType)就不是一個空值了.
如果沒有搜索到這些擴展, 就會預設反射的方式來創建控制器.
Demo: 值的註意的是, 這些擴展都是要實現 IDependencyResolve 介面的, 此例子是從Autofac.Integration.Mvc.dll中來的
public class AutofacDependencyResolver : IDependencyResolver { // Fields private readonly Action<ContainerBuilder> _configurationAction; private readonly ILifetimeScope _container; private ILifetimeScopeProvider _lifetimeScopeProvider; // Methods public AutofacDependencyResolver(ILifetimeScope container); public AutofacDependencyResolver(ILifetimeScope container, ILifetimeScopeProvider lifetimeScopeProvider); public AutofacDependencyResolver(ILifetimeScope container, Action<ContainerBuilder> configurationAction); public AutofacDependencyResolver(ILifetimeScope container,
ILifetimeScopeProvider lifetimeScopeProvider,
Action<ContainerBuilder> configurationAction); public object GetService(Type serviceType); public IEnumerable<object> GetServices(Type serviceType); // Properties public ILifetimeScope ApplicationContainer { get; } public static AutofacDependencyResolver Current { get; } public ILifetimeScope RequestLifetimeScope { get; } }
2. Execute()
這裡執行的是ControllerBase的Execute方法. 是一個虛方法. 這裡的方法, 留到下一篇分析了.
protected virtual void Execute(RequestContext requestContext) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (requestContext.HttpContext == null) { throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext"); } this.VerifyExecuteCalledOnce(); this.Initialize(requestContext); using (ScopeStorage.CreateTransientScope()) { this.ExecuteCore(); } }
3. ReleaseController()
這個方法, 顧名思義, 是釋放資源的. 來看一下, DefaultControllerFactory 類中的此方法
public virtual void ReleaseController(IController controller) { IDisposable disposable = controller as IDisposable; if (disposable != null) { disposable.Dispose(); } }
從上面的分析, 基本可以看到控制器的創建過程, 至於他的執行過程, 就放到下一篇去了, 內容還是很多的.
二、擴展
從上面的分析能看到這裡有一個依賴註入擴展, 那麼下麵, 就這個依賴註入擴展, 來舉一個小例子.(殘破的例子, 別介意, 能演示功能的)
用的Autofac, 不過與上面不同, 並沒有引用上面的程式集, 只引用Autofac.dll一個程式集就可以了.
我先從之前Autofac篇章, 弄了一個Helper類過來, 精簡一下.
public class IocContainer { private static ContainerBuilder builder; private static IContainer container; static IocContainer() { builder = new ContainerBuilder(); } public static void RegisterTypes(params Type[] types) { builder.RegisterTypes(types); } public static void Build() { container = builder.Build(); } public static object Create(Type t) { return container.Resolve(t); } }
然後, 我建了一個Resolver.
public class MyDependencyResolver : IDependencyResolver { public object GetService(Type serviceType) { bool flag = true; string path = @"F:\MVC解析\MyControllerFac\1.txt"; if (!File.Exists(path)) { File.Create(path).Close(); } try { return IocContainer.Create(serviceType); } catch (Exception) { flag = false; //這裡需要註意, 需要返回一個null值, 否則會報錯 return null; } finally { using (FileStream fs = new FileStream(path, FileMode.Append)) { var msg = flag ? "命中" : "飄過"; byte[] bt = System.Text.Encoding.UTF8.GetBytes(serviceType.ToString() + " : " + msg + "\r\n"); fs.Write(bt, 0, bt.Length);
fs.Close(); } } } public IEnumerable<object> GetServices(Type serviceType) { var res = new List<object>(); try { var obj = IocContainer.Create(serviceType); res.Add(obj); } catch (Exception) { } return res; } }
現在前置工作做好了, 可以去MVC中, 註冊我自己的方法了.
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles);
//這裡我就省懶了, 並沒有寫到單獨的方法中取 IocContainer.RegisterTypes(System.Reflection.Assembly.Load("MyMvc").GetTypes()); IocContainer.Build(); DependencyResolver.SetResolver(new MyDependencyResolver()); }
OK, 萬事俱備, 只差測試. 跑起來吧
頁面能正常訪問, 那麼是不是走的我哪裡呢? 從頁面上肯定看不出來, 所以我加了點東西, 看一下1.txt
從這裡來看, 並不是只有 HomeController 走了我自定義的那個方式, 這也是為什麼一定要返回一個null了. 對於這些飄過的, 就只能使用MVC預設的方式了. 還是很神奇的.