书接上文,我们继续跟踪CreateView方法,发现它是一个抽象方法,好吧,我们发现BuildManagerViewEngine继承了VirtualPathProviderViewEngine,但是它并没有重写了CreateView方法,不要慌,我们继续摸索,发现RazorViewEngine和WebFormViewEngine(先不管他)又继承了BuildManagerViewEngine,在这里我们只看RazorViewEngine的CreateView方法重写(代码段1)。
protected override IViewCreateView(ControllerContext controllerContext, string viewPath, stringmasterPath) { var view = newRazorView(controllerContext, viewPath, layoutPath: masterPath, runViewStartPages: true,viewStartFileExtensions: FileExtensions, viewPageActivator: ViewPageActivator) { DisplayModeProvider =DisplayModeProvider }; return view; }
代码段 1
代码段1得到了一个IView对象,进而使得“带你读开源—ASP.NET_MVC(四)”代码段10得到一个ViewEngineResult,我们再转到“带你读开源—ASP.NET_MVC(四)”代码段2中的语句【result = FindView(context);View = result.View;】,到此我们便得到了一个View对象,进而调用View.Render(viewContext, writer)方法,而其中的writer 又是什么东东?我们找到语句【TextWriter writer =context.HttpContext.Response.Output;】,豁然开朗不?我们久违的、日思夜想的Response终于出现了,有了它,我们就可以把HTML响应渲染回送到浏览器,至此,从浏览器发出HTTP请求,到服务器发回HTML响应,构成完整的回路。我靠,累死我了!
我们再看代码段2,这是一个IView接口的定义,该接口仅有一个方法Render,用以向浏览器渲染HTML页面。
<pre name="code" class="csharp"> public interface IView { void Render(ViewContext viewContext, TextWriterwriter); }
代码段 2
只有一个类实现了IView接口,这个类就是BuildManagerCompiledView,我们看Render方法的具体实现,见代码段3。
public virtual void Render(ViewContextviewContext, TextWriter writer) { if (viewContext == null) { throw newArgumentNullException("viewContext"); } object instance = null; Type type =BuildManager.GetCompiledType(ViewPath); if (type != null) { instance =ViewPageActivator.Create(_controllerContext, type); } if (instance == null) { throw newInvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.CshtmlView_ViewCouldNotBeCreated, ViewPath)); } RenderView(viewContext, writer,instance); }
代码段 3
代码段3中有两个地方需要我们注意:
①【instance= ViewPageActivator.Create(_controllerContext, type);】
②【RenderView(viewContext,writer, instance);】
其中,①是实例化视图类,②是渲染视图。为了突出主线,我们先分析②。我们发现RenderView是一个抽象方法,凭经验BuildManagerCompiledView类应该有子类重写了RenderView方法,在源码中寻找,发现它有两个子类,即WebFormView和RazorView,见到这两个类开心吧?呵呵!我们在这里先只分析RazorView的RenderView方法重写,见代码段4。
protected override voidRenderView(ViewContext viewContext, TextWriter writer, object instance) { if (writer == null) { throw newArgumentNullException("writer"); } WebViewPage webViewPage = instanceas WebViewPage; if (webViewPage == null) { throw newInvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.CshtmlView_WrongViewBase, ViewPath)); } // An overriden master layout mighthave been specified when the ViewActionResult got returned. // We need to hold on to it so thatwe can set it on the inner page once it has executed. webViewPage.OverridenLayoutPath =LayoutPath; webViewPage.VirtualPath = ViewPath; webViewPage.ViewContext =viewContext; webViewPage.ViewData =viewContext.ViewData; webViewPage.InitHelpers(); if (VirtualPathFactory != null) { webViewPage.VirtualPathFactory= VirtualPathFactory; } if (DisplayModeProvider != null) { webViewPage.DisplayModeProvider= DisplayModeProvider; } WebPageRenderingBase startPage =null; if (RunViewStartPages) { startPage =StartPageLookup(webViewPage, RazorViewEngine.ViewStartFileName, ViewStartFileExtensions); } webViewPage.ExecutePageHierarchy(new WebPageContext(context:viewContext.HttpContext, page: null, model: null), writer, startPage); }
代码段 4
在代码段4中找到语句【webViewPage.ExecutePageHierarchy(new WebPageContext(context:viewContext.HttpContext, page: null, model: null), writer, startPage);】,这句是执行渲染的核心语句。继续跟踪进入webViewPage.ExecutePageHierarchy,见代码段5。
// This method is only used byWebPageBase to allow passing in the view context and writer. public voidExecutePageHierarchy(WebPageContext pageContext, TextWriter writer,WebPageRenderingBase startPage) { PushContext(pageContext, writer); if (startPage != null) { if (startPage != this) { var startPageContext =WebPageContext.CreateNestedPageContext<object>(parentContext:pageContext, pageData: null, model: null, isLayoutPage: false); startPageContext.Page =startPage; startPage.PageContext = startPageContext; } startPage.ExecutePageHierarchy(); } else { ExecutePageHierarchy(); } PopContext(); }
代码段 5
在代码段5中找到ExecutePageHierarchy方法,进入其定义,见代码段6。
[SuppressMessage("Microsoft.Design","CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Wereally don't care if SourceHeader fails, and we don't want it to fail any realrequests ever")] public override voidExecutePageHierarchy() { // Unlike InitPages, for a WebPagethere is no hierarchy - it is always // the last file to execute in thechain. There can still be layout pages // and partial pages, but they arenever part of the hierarchy. // (add server header for falcondebugging) // call to MapPath() is expensive.If we are not emiting source files to header, // don't bother to populate theSourceFiles collection. This saves perf significantly. if(WebPageHttpHandler.ShouldGenerateSourceHeader(Context)) { try { string vp = VirtualPath; if (vp != null) { string path =Context.Request.MapPath(vp); if (!path.IsEmpty()) { PageContext.SourceFiles.Add(path); } } } catch { // we really don't care ifthis ever fails, so we swallow all exceptions } } TemplateStack.Push(Context, this); try { // Execute thedeveloper-written code of the WebPage Execute(); } finally { TemplateStack.Pop(Context); } }
代码段 6
在代码段6中找到语句Execute()方法,在这个方法上方有一条注释“//Execute thedeveloper-written code of the WebPage”,大概意思是“执行开发者编写的页面代码”,明白了吧?这句的功能就是执行你编写的视图代码,即.cshtml文件。那么肯定有人要提出疑问了,.cshtml文件怎么执行?它不是一段html脚本么?不错,.cshtml文件确实是一段html脚本,不能被直接执行,在此留一个悬念[1]。
未完待续。。。