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

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

C# code

?

1
2
3
4
5
6
7
8
9
        protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
        {
            var view = new RazorView(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页面。

C# code

?

1
2
3
4
    public interface IView
    {
        void Render(ViewContext viewContext, TextWriter writer);
    }

 

代码段 2

只有一个类实现了IView接口,这个类就是BuildManagerCompiledView,我们看Render方法的具体实现,见代码段3。

C# code

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
        public virtual void Render(ViewContext viewContext, TextWriter writer)
        {
            if (viewContext == null)
            {
                throw new ArgumentNullException("viewContext");
            }
            object instance = null;
            Type type = BuildManager.GetCompiledType(ViewPath);
            if (type != null)
            {
                instance = ViewPageActivator.Create(_controllerContext, type);
            }
            if (instance == null)
            {
                throw new InvalidOperationException(
                    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。

C# code

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 protected override void RenderView(ViewContext viewContext, TextWriter writer, object instance)
        {
            if (writer == null)
            {
                throw new ArgumentNullException("writer");
            }
            WebViewPage webViewPage = instance as WebViewPage;
            if (webViewPage == null)
            {
                throw new InvalidOperationException(
                    String.Format(
                        CultureInfo.CurrentCulture,
                        MvcResources.CshtmlView_WrongViewBase,
                        ViewPath));
            }
            // An overriden master layout might have been specified when the ViewActionResult got returned.
            // We need to hold on to it so that we 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。

C# code

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 // This method is only used by WebPageBase to allow passing in the view context and writer.
        public void ExecutePageHierarchy(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。

C# code

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  [SuppressMessage("Microsoft.Design""CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We really don't care if SourceHeader fails, and we don't want it to fail any real requests ever")]
        public override void ExecutePageHierarchy()
        {
            // Unlike InitPages, for a WebPage there is no hierarchy - it is always
            // the last file to execute in the chain. There can still be layout pages
            // and partial pages, but they are never part of the hierarchy.
            // (add server header for falcon debugging)
            // call to MapPath() is expensive. If we are not emiting source files to header, 
            // don't bother to populate the SourceFiles 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 if this ever fails, so we swallow all exceptions
                }
            }
            TemplateStack.Push(Context, this);
            try
            {
                // Execute the developer-written code of the WebPage
                Execute();
            }
            finally
            {
                TemplateStack.Pop(Context);
            }
        }

 

代码段 6

在代码段6中找到语句Execute()方法,在这个方法上方有一条注释“//Execute the developer-written code of the WebPage”,大概意思是“执行开发者编写的页面代码”,明白了吧?这句的功能就是执行你编写的视图代码,即.cshtml文件。那么肯定有人要提出疑问了,.cshtml文件怎么执行?它不是一段html脚本么?不错,.cshtml文件确实是一段html脚本,不能被直接执行,在此留一个悬念[1]。
未完待续。。。

xiaotmh
  • 版权声明: 本文源自 CSDN CKAOS, 于9个月前,由整理发表,共 7452字。
  • 原文链接:点此查看原文

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: