带你读开源—ASP.NET_MVC(四)

        上篇聊到ActionResult的ExecuteResult方法,今天继续。

        我们首先看一下ActionResult的定义(代码段1),它是一个抽象类,只有一个抽象方法ExecuteResult。

    public abstract class ActionResult
    {
        public abstract voidExecuteResult(ControllerContext context);
    }

代码段 1

        ActionResult是个很重要的话题,MVC在Action中没有直接生成并回送HTML响应,而是返回一个ActionResult,再由MVC框架调用它的ExecuteResult方法。这是一种设计模式的应用,即“命令模式”,它把提出请求和执行请求分开,解除它们之间的耦合。有关命令模式,请自行找度娘脑补。

        ActionResult有N多的子类,包括ViewResultBase、ContentResult、FileResult、HttpStatusCodeResult、JavaScriptResult、JsonResult、RedirectResult、RedirectToRouteResult、AtomEntryActionResult、AtomFeedActionResult、AtomServiceDocumentActionResult、DataContractJsonActionResult、DataContractXmlActionResult、MultiFormatActionResult、ResourceErrorActionResult、ResourceRedirectToRouteResult。这些子类都是针对不同的需求定义的,最常见的是ViewResultBase的子类ViewResult(当然,PartialViewResult也是ViewResultBase的子类)。不过ViewResult并没有重写ExecuteResult方法,而是由其父类ViewResultBase进行的重写,见代码段2。

        public override voidExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw newArgumentNullException("context");
            }
            if (String.IsNullOrEmpty(ViewName))
            {
                ViewName =context.RouteData.GetRequiredString("action");
            }
 
            ViewEngineResult result = null;
 
            if (View == null)
            {
                result = FindView(context);
                View = result.View;
            }
 
            TextWriter writer =context.HttpContext.Response.Output;
            ViewContext viewContext = newViewContext(context, View, ViewData, TempData, writer);
            View.Render(viewContext, writer);
 
            if (result != null)
            {
               result.ViewEngine.ReleaseView(context, View);
            }
        }

代码段 2

        代码段2中的语句【View.Render(viewContext, writer)】就是把HTML渲染并发回浏览器的核心代码。这里就开始引入万人敬仰的视图引擎机制了,我们看语句【result = FindView(context)】,跟踪到FindView的定义,哦,它是一个抽象方法,好吧,去他的子类ViewResult中找,见代码段3。

        protected override ViewEngineResultFindView(ControllerContext context)
        {
            ViewEngineResult result =ViewEngineCollection.FindView(context, ViewName, MasterName);
            if (result.View != null)
            {
                return result;
            }
 
            // we need to generate an exceptioncontaining all the locations we searched
            StringBuilder locationsText = newStringBuilder();
            foreach (string location inresult.SearchedLocations)
            {
                locationsText.AppendLine();
                locationsText.Append(location);
            }
            throw newInvalidOperationException(String.Format(CultureInfo.CurrentCulture,
                                                             MvcResources.Common_ViewNotFound, ViewName, locationsText));
        }

代码段 3

        FindView方法的作用是找到指定的视图引擎,在代码段3中找到语句【ViewEngineResult result = ViewEngineCollection.FindView(context,ViewName, MasterName)】,跟踪进入ViewEngineCollection.FindView的定义(代码段4)。

        public virtual ViewEngineResultFindView(ControllerContext controllerContext, string viewName, stringmasterName)
        {
            if (controllerContext == null)
            {
                throw newArgumentNullException("controllerContext");
            }
            if (String.IsNullOrEmpty(viewName))
            {
                throw newArgumentException(MvcResources.Common_NullOrEmpty, "viewName");
            }
 
            return Find(e =>e.FindView(controllerContext, viewName, masterName, true),
                        e =>e.FindView(controllerContext, viewName, masterName, false));
        }

代码段 4

        跟踪进入Find方法(代码段5),这个方法的作用是在视图引擎集合中,用Func<IViewEngine,ViewEngineResult> lookup这个委托来搜索当前使用的视图引擎。

 

       private ViewEngineResultFind(Func<IViewEngine, ViewEngineResult> lookup, bool trackSearchedPaths)
        {
            // Returns
            //    1st result
            // OR list of searched paths (iftrackSearchedPaths == true)
            // OR null
            ViewEngineResult result;
 
            List<string> searched = null;
            if (trackSearchedPaths)
            {
                searched = newList<string>();
            }
 
            foreach (IViewEngine engine inCombinedItems)
            {
                if (engine != null)
                {
                    result = lookup(engine);
 
                    if (result.View != null)
                    {
                        return result;
                    }
 
                    if (trackSearchedPaths)
                    {
                       searched.AddRange(result.SearchedLocations);
                    }
                }
            }
 
            if (trackSearchedPaths)
            {
                // Remove duplicate searchpaths since multiple view engines could have potentially looked at the samepath
                return newViewEngineResult(searched.Distinct().ToList());
            }
            else
            {
                return null;
            }
        }

代码段 5

        在代码段5中有一个CombinedItems,它代表视图引擎集合,它的定义见代码段6。

        internal IViewEngine[] CombinedItems
        {
            get
            {
                IViewEngine[] combinedItems =_combinedItems;
                if (combinedItems == null)
                {
                    combinedItems =MultiServiceResolver.GetCombined<IViewEngine>(Items, _dependencyResolver);
                    _combinedItems =combinedItems;
                }
                return combinedItems;
            }
        }

代码段 6

        CombinedItems实际上指的是ViewEngineCollection中的内容项,而ViewEngineCollection的内容又是在哪里填充的呢?我们看代码段3中的这句【ViewEngineResult result = ViewEngineCollection.FindView(context,ViewName, MasterName)】,里面有ViewEngineCollection,转到其定义,见代码段7。

        [SuppressMessage("Microsoft.Usage","CA2227:CollectionPropertiesShouldBeReadOnly", Justification ="This entire type is meant to be mutable.")]
        public ViewEngineCollectionViewEngineCollection
        {
            get { return _viewEngineCollection?? ViewEngines.Engines; }
            set { _viewEngineCollection =value; }
        }

代码段 7

        可见ViewEngineCollection来自于ViewEngines.Engines,接着转到ViewEngines.Engines的定义,见代码段8。看到了吧!原来秘密在此,ViewEngineCollection这个集合的内容为预置的WebFormViewEngine和RazorViewEngine,也就是我们所熟知的WebForm视图引擎和Razor视图引擎。

    public static class ViewEngines
    {
        private static readonlyViewEngineCollection _engines = new ViewEngineCollection
        {
            new WebFormViewEngine(),
            new RazorViewEngine(),
        };
 
        public static ViewEngineCollectionEngines
        {
            get { return _engines; }
        }
}

代码段 8

        我们再回看一下代码段4,可以看出【e => e.FindView(controllerContext, viewName, masterName, true)】这个Lamda表达式(本质是委托)作为Find方法的第一个参数lookup进行传递,也就是说,用这个Lamda表达式是判断某个视图引擎是不是当前使用的视图引擎。继续跟踪e.FindView,进入其定义(代码段9),发现FindView是接口IViewEngine的一个方法。IViewEngine是视图引擎的接口,我们已经到达视图引擎的核心了,鸡冻吧?

    public interface IViewEngine
    {
        ViewEngineResultFindPartialView(ControllerContext controllerContext, string partialViewName,bool useCache);
        ViewEngineResultFindView(ControllerContext controllerContext, string viewName, stringmasterName, bool useCache);
        void ReleaseView(ControllerContextcontrollerContext, IView view);
}

代码段 9

        好了,我们看看都有谁实现了IViewEngine接口,找了半天发现只有VirtualPathProviderViewEngine实现了IViewEngine接口,见代码段10。

        public virtual ViewEngineResultFindView(ControllerContext controllerContext, string viewName, stringmasterName, bool useCache)
        {
            if (controllerContext == null)
            {
                throw newArgumentNullException("controllerContext");
            }
            if (String.IsNullOrEmpty(viewName))
            {
                throw newArgumentException(MvcResources.Common_NullOrEmpty, "viewName");
            }
 
            string[] viewLocationsSearched;
            string[] masterLocationsSearched;
 
            string controllerName =controllerContext.RouteData.GetRequiredString("controller");
            string viewPath =GetPath(controllerContext, ViewLocationFormats, AreaViewLocationFormats,"ViewLocationFormats", viewName, controllerName, CacheKeyPrefixView,useCache, out viewLocationsSearched);
            string masterPath =GetPath(controllerContext, MasterLocationFormats, AreaMasterLocationFormats,"MasterLocationFormats", masterName, controllerName,CacheKeyPrefixMaster, useCache, out masterLocationsSearched);
 
            if (String.IsNullOrEmpty(viewPath)|| (String.IsNullOrEmpty(masterPath) &&!String.IsNullOrEmpty(masterName)))
            {
                return newViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched));
            }
 
            return newViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this);
        }

代码段 10

        跟踪CreateView方法,发现它是一个抽象方法,好吧,我们发现BuildManagerViewEngine继承了VirtualPathProviderViewEngine,但是它并没有重写了CreateView方法,不要慌,我们继续摸索,发现RazorViewEngine和WebFormViewEngine(先不管他)又继承了BuildManagerViewEngine,在这里我们只看RazorViewEngine的CreateView方法重写(代码段11)。

        protected override IViewCreateView(ControllerContext controllerContext, string viewPath, stringmasterPath)
        {
            var view = new RazorView(controllerContext,viewPath,
                                    layoutPath: masterPath, runViewStartPages: true,viewStartFileExtensions: FileExtensions, viewPageActivator: ViewPageActivator)
            {
                DisplayModeProvider = DisplayModeProvider
            };
            return view;
        }

代码段 11

        代码段11得到了一个IView对象,进而使得代码段10得到一个ViewEngineResult,我们再转到代码段2中的语句【result = FindView(context);View = result.View;】,到此我们便得到了一个View对象,进而调用View.Render(viewContext, writer)方法,而其中的writer 又是什么东东?我们找到语句【TextWriter writer =context.HttpContext.Response.Output;】,豁然开朗不?我们久违的、日思夜想的Response终于出现了,有了它,我们就可以把HTML响应渲染回送到浏览器,至此,从浏览器发出HTTP请求,到服务器发回HTML响应,构成完整的回路,大功告成。我靠,累死我了!

        未完待续。。。

你可能感兴趣的:(mvc,.net,开源,asp.net)