Asp.Net MVC<八>:View的呈现

ActionResult

原则上任何类型的响应都可以利用当前的HttpResponse来完成。但是MVC中我们一般将针对请求的响应实现在一个ActionResult对象中。

public abstract class ActionResult
{
    protected ActionResult();

    public abstract void ExecuteResult(ControllerContext context);
} 

ViewResult和ViewEngine

IViewEngine

viewResult通过ViewEngine实现对View的获取、激活和呈现。

public interface IViewEngine
{
    ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache);

    ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache);

    void ReleaseView(ControllerContext controllerContext, IView view);
}

 现有两种实现:WebFormViewEngine(.aspx,.ascx),RazorViewEngine(.cshtml/vbhtml)

public void Index()
{
    ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, "NonExistView", null);
    foreach (var item in result.SearchedLocations)
    {
        Response.Write(item + "<br/>");
    }
}

  

因为WebFormViewEngine排在RazorViewEngine之前,所以前者被优先使用。可以用 ViewEngines.Engines.RemoveAt(0);来移除对WebFormViewEngine的调用。

ViewResult

public class ViewResult : ViewResultBase
{
    public string MasterName { get; set; }

    protected override ViewEngineResult FindView(ControllerContext context);
}

ViewResultBase 

public abstract class ViewResultBase: ActionResult
{
    public object Model { get; }

    //     临时数据。
    public TempDataDictionary TempData { get; set; }

    //     视图。
    public IView View { get; set; }

    //     视图包
    public dynamic ViewBag { get; }

    //     视图数据。
    public ViewDataDictionary ViewData { get; set; }

    //     视图引擎的集合。
    public ViewEngineCollection ViewEngineCollection { get; set; }

    //     视图的名称。
    public string ViewName { get; set; }

    //     视图引擎。
    protected abstract ViewEngineResult FindView(ControllerContext context);
    public override void ExecuteResult(ControllerContext context)
    {
        if (string.IsNullOrEmpty(this.ViewName))
        {
            this.ViewName = context.RouteData.GetRequiredString("action");
        }

        ViewEngineResult result = null;
        if(this.View == null)
        {
            result = this.FindView(context);
            this.View = result.View;
        }

        TextWriter output = context.HttpContext.Response.Output;
        ViewContext viewContext = new ViewContext(context, this.View, this.ViewData, this.TempData, output);
        this.View.Render(viewContext, output);

        if(result != null)
        {
            result.ViewEngine.ReleaseView(context, this.View);
        }
    }
}

  抽象类Controller中的几个重载方法

 View的编译原理

 Asp.net mvc默认情况下采用动态编译的方式对View文件实施编译。当我们在部署的时候,需要对.cshtml或.vbhtml文件进行打包。

针对某个文件的第一次访问会触发针对它的编译,一个View会被编译成一个具体的类型。View文件的每一次修改都会导致再一次编译。和.aspx页面一样的编译方式,默认是以目录为单位的,也就是同一个目录下的多个View被编译到同一个程序集中。

View程序集

public static class HtmlHelperExtensions
{
    public static MvcHtmlString ListViewAssemblies(this HtmlHelper helper)
    {
        TagBuilder ul = new TagBuilder("ul");
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.StartsWith("App_Web_")))
        {
            TagBuilder li = new TagBuilder("li");
            li.InnerHtml = assembly.FullName;
            ul.InnerHtml += li.ToString();
        }
        return new MvcHtmlString(ul.ToString());
    }
}

View:  

<div>当前View类型:@this.GetType().AssemblyQualifiedName</div>
<div>当前加载的View程序集:</div>
@Html.ListViewAssemblies()

页面展示: 

 

BuildManager

Response.Write(BuildManager.GetCompiledType("~/Views/Bar/Action1.cshtml") + "<br/>");
Response.Write(BuildManager.GetCompiledType("~/Views/Bar/Action2.cshtml") + "<br/>");

Response.Write(BuildManager.TargetFramework.FullName+ "<br/>");
Response.Write(BuildManager.GetCompiledCustomString("~/Views/Foo/Action1.cshtml") + "<br/>");
Response.Write(BuildManager.GetCompiledAssembly("~/Views/Foo/Action1.cshtml").Location + "<br/>");

页面展示:

WebViewPage的继承树

由.cshtml文件编译后产生的类文件

namespace ASP
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Web;
    using System.Web.Helpers;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.WebPages;
    using System.Web.Mvc;
    using System.Web.Mvc.Ajax;
    using System.Web.Mvc.Html;
    using System.Web.Optimization;
    using System.Web.Routing;
    using MvcApp;


    public class _Page_Views_Foo_Action1_cshtml : System.Web.Mvc.WebViewPage<dynamic>
    {
        protected ASP.global_asax ApplicationInstance
        {
            get
            {
                return ((ASP.global_asax)(Context.ApplicationInstance));
            }
        }

        public override void Execute()
        {
            BeginContext("~/Views/Foo/Action1.cshtml", 0, 14, true);
            WriteLiteral("<div>当前View类型:");
            EndContext("~/Views/Foo/Action1.cshtml", 0, 14, true);
            BeginContext("~/Views/Foo/Action1.cshtml", 15, 36, false);
            Write(this.GetType().AssemblyQualifiedName);
            EndContext("~/Views/Foo/Action1.cshtml", 15, 36, false);
            BeginContext("~/Views/Foo/Action1.cshtml", 51, 34, true);
            WriteLiteral("</div>
<div>当前加载的View程序集:</div>
");
            EndContext("~/Views/Foo/Action1.cshtml", 51, 34, true);
            BeginContext("~/Views/Foo/Action1.cshtml", 86, 25, false);

            Write(Html.ListViewAssemblies());
            EndContext("~/Views/Foo/Action1.cshtml", 86, 25, false);
            BeginContext("~/Views/Foo/Action1.cshtml", 111, 2, true);
            WriteLiteral("
");
            EndContext("~/Views/Foo/Action1.cshtml", 111, 2, true);
        }
    }
}

 View编译后的生成的类型是WebViewPage<TModel>的子类,WebViewPage<TModel>是WebViewPage的子类,泛型TModel是View的Model类型。

WebPageExecutingBase

public abstract class WebPageExecutingBase
{
    public virtual dynamic App { get; }
    public virtual HttpApplicationStateBase AppState { get; }
    public virtual HttpContextBase Context { get; set; }
    public virtual string VirtualPath { get; set; }
    public virtual IVirtualPathFactory VirtualPathFactory { get; set; }

    public abstract void Execute();
    public virtual string Href(string path, params object[] pathParts);
    public virtual string NormalizePath(string path);
    protected internal virtual TextWriter GetOutputWriter();
    protected internal virtual string NormalizeLayoutPagePath(string layoutPagePath);

    //动态内容
    public abstract void Write(object value);
    public abstract void Write(HelperResult result);
    public static void WriteTo(TextWriter writer, object content);
    public static void WriteTo(TextWriter writer, HelperResult content);
    //静态内容
    public abstract void WriteLiteral(object value);
    public static void WriteLiteralTo(TextWriter writer, object content);

    public virtual void WriteAttribute(string name, PositionTagged<string> prefix, PositionTagged<string> suffix, params AttributeValue[] values);
    public virtual void WriteAttributeTo(TextWriter writer, string name, PositionTagged<string> prefix, PositionTagged<string> suffix, params AttributeValue[] values);
    protected internal virtual void WriteAttributeTo(string pageVirtualPath, TextWriter writer, string name, PositionTagged<string> prefix, PositionTagged<string> suffix, params AttributeValue[] values);

    protected internal void BeginContext(int startPosition, int length, bool isLiteral);
    protected internal void BeginContext(string virtualPath, int startPosition, int length, bool isLiteral);
    protected internal void BeginContext(TextWriter writer, int startPosition, int length, bool isLiteral);
    protected internal void BeginContext(TextWriter writer, string virtualPath, int startPosition, int length, bool isLiteral);
    protected internal void EndContext(int startPosition, int length, bool isLiteral);
    protected internal void EndContext(string virtualPath, int startPosition, int length, bool isLiteral);
    protected internal void EndContext(TextWriter writer, int startPosition, int length, bool isLiteral);
    protected internal void EndContext(TextWriter writer, string virtualPath, int startPosition, int length, bool isLiteral);
}

  

WebPageRenderingBase 

public abstract class WebPageRenderingBase : WebPageExecutingBase, ITemplateFile
{
    public virtual Cache Cache { get; }
    public string Culture { get; set; }
    public virtual bool IsAjax { get; }
    public virtual bool IsPost { get; }
    public abstract string Layout { get; set; }
    //     An object that contains page data.
    public abstract dynamic Page { get; }
    public abstract IDictionary<object, dynamic> PageData { get; }
    public WebPageContext PageContext { get; }
    public ProfileBase Profile { get; }
    public virtual HttpRequestBase Request { get; }
    public virtual HttpResponseBase Response { get; }
    public virtual HttpServerUtilityBase Server { get; }
    public virtual HttpSessionStateBase Session { get; }
    public virtual TemplateFileInfo TemplateInfo { get; }
    public string UICulture { get; set; }
    public virtual IList<string> UrlData { get; }
    public virtual IPrincipal User { get; internal set; }
    protected internal IDisplayMode DisplayMode { get; }

    public abstract void ExecutePageHierarchy();
    public abstract HelperResult RenderPage(string path, params object[] data);
}

  ExecutePageHierarchy负责整个页面内容的输出,

     View自身内容通过重写Execute方法来输出。

WebPageBase 

public abstract class WebPageBase : WebPageRenderingBase
{
    //其他成员……   

    //     Gets or sets the path of a layout page.
    public override string Layout { get; set; }
    public TextWriter Output { get; }	
	//     Returns the text writer instance that is used to render the page.
    protected internal override TextWriter GetOutputWriter();
	
    //     Gets the stack of System.IO.TextWriter objects for the current page context.
    public Stack<TextWriter> OutputStack { get; }
	//     Returns and removes the context from the top of the System.Web.WebPages.WebPageBase.OutputStack
    //     instance.
    public void PopContext();
	//     Inserts the specified context at the top of the System.Web.WebPages.WebPageBase.OutputStack
    //     instance.
    public void PushContext(WebPageContext pageContext, TextWriter writer);

    //     Executes the code in a set of dependent web pages.
    public override void ExecutePageHierarchy();
    //     Executes the code in a set of dependent web pages by using the specified parameters.
    public void ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer);
    //     Executes the code in a set of dependent web pages by using the specified context,
    //     writer, and start page.
    public void ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage);

    //     Returns a value that indicates whether the specified section is defined in the
    //     page.
    public bool IsSectionDefined(string name);
    //     Called by content pages to create named content sections.
    public void DefineSection(string name, SectionWriter action);
	
    //     In layout pages, renders the portion of a content page that is not within a named
    //     section.
    public HelperResult RenderBody();
    //     Renders the content of one page within another page.
    public override HelperResult RenderPage(string path, params object[] data);
    //     In layout pages, renders the content of a named section.
    public HelperResult RenderSection(string name);
    //     In layout pages, renders the content of a named section and specifies whether
    //     the section is required.
    public HelperResult RenderSection(string name, bool required);

    //     Writes the specified object as an HTML-encoded string.
    public override void Write(object value);
    //     Writes the specified System.Web.WebPages.HelperResult object as an HTML-encoded
    //     string.
    public override void Write(HelperResult result);
    //     Writes the specified object without HTML-encoding it first.
    public override void WriteLiteral(object value);
}

  WebPageBase 实现了抽象方法ExecutePageHierarchy,定义了额外的两个ExecutePageHierarchy方法重载,参数startPage表示定义在“_ViewStart.cshtml”文件的开始页面。

    最初呈现的内容来源于3个部分:布局文件,开始页面和View自身的内容。完整页面内容的呈现通过调用ExecutePageHierarchy方法来完成。

    WebPageBase 实现了抽象方法Write/WriteLiteral和抽象属性 Layout。

    定义了在布局文件上输出Section的方法。

WebViewPage

public abstract class WebViewPage : WebPageBase, IViewDataContainer, IViewStartPageChild
{
    //     The System.Web.Mvc.AjaxHelper object that is used to render HTML using Ajax.
    public AjaxHelper<object> Ajax { get; set; }

    //     The System.Web.HttpContext object that is associated with the page.
    public override HttpContextBase Context { get; set; }

    //     The System.Web.Mvc.HtmlHelper object that is used to render HTML elements.
    public HtmlHelper<object> Html { get; set; }
	
    public object Model { get; }
    public TempDataDictionary TempData { get; }
    public UrlHelper Url { get; set; }
    public dynamic ViewBag { get; }
    public ViewContext ViewContext { get; set; }
    public ViewDataDictionary ViewData { get; set; }

    //     Runs the page hierarchy for the ASP.NET Razor execution pipeline.
    public override void ExecutePageHierarchy();

    //     Initializes the System.Web.Mvc.AjaxHelper, System.Web.Mvc.HtmlHelper, 
    //     and System.Web.Mvc.UrlHelper classes.
    public virtual void InitHelpers();

    //     Sets the view context and view data for the page.
    protected override void ConfigurePage(WebPageBase parentPage);

    protected virtual void SetViewData(ViewDataDictionary viewData);
}

  

WebViewPage<TModel>

public abstract class WebViewPage<TModel> : WebViewPage
{
    public AjaxHelper<TModel> Ajax { get; set; }

    public HtmlHelper<TModel> Html { get; set; }

    public TModel Model { get; }

    public ViewDataDictionary<TModel> ViewData { get; set; }

    public override void InitHelpers();

    protected override void SetViewData(ViewDataDictionary viewData);
}

  提供针对泛型参数TModel的属性Ajax, Html, Model, ViewData, 而初始化它们的InitHelpers和SetViewData方法也被重写

 View的呈现

IView

对应View引擎来说,View通过IView来表示

public interface IView
{
    void Render(ViewContext viewContext, TextWriter writer);
}

View的呈现体现在对WebViewPage对象的激活上。

 在Asp.net mvc 中Razor引擎下的View通过RazorView对象来表示,WebForm引擎的View则通过WebFormView对象表示,二者都继承自BuildManagerCompiledView。

RazorView 实现

BuildManagerCompiledView 

public abstract class BuildManagerCompiledView : IView
{
    internal IViewPageActivator ViewPageActivator;
    private IBuildManager _buildManager;
    private ControllerContext _controllerContext;
    internal IBuildManager BuildManager
    {
        get
        {
            if (this._buildManager == null)
            {
                this._buildManager = new BuildManagerWrapper();
            }
            return this._buildManager;
        }
        set
        {
            this._buildManager = value;
        }
    }
    public string ViewPath { get; protected set; }

    protected BuildManagerCompiledView(ControllerContext controllerContext, string viewPath) : this(controllerContext, viewPath, null)
    {
    }

    protected BuildManagerCompiledView(ControllerContext controllerContext, string viewPath, IViewPageActivator viewPageActivator) : this(controllerContext, viewPath, viewPageActivator, null)
    {
    }
    internal BuildManagerCompiledView(ControllerContext controllerContext, string viewPath, IViewPageActivator viewPageActivator, IDependencyResolver dependencyResolver)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        if (string.IsNullOrEmpty(viewPath))
        {
            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewPath");
        }
        this._controllerContext = controllerContext;
        this.ViewPath = viewPath;
        this.ViewPageActivator = (viewPageActivator ?? new BuildManagerViewEngine.DefaultViewPageActivator(dependencyResolver));
    }

    public virtual void Render(ViewContext viewContext, TextWriter writer)
    {
        if (viewContext == null)
        {
            throw new ArgumentNullException("viewContext");
        }
        object obj = null;
        Type compiledType = this.BuildManager.GetCompiledType(this.ViewPath);
        if (compiledType != null)
        {
            obj = this.ViewPageActivator.Create(this._controllerContext, compiledType);
        }
        if (obj == null)
        {
            throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.CshtmlView_ViewCouldNotBeCreated, new object[]
            {
            this.ViewPath
            }));
        }
        this.RenderView(viewContext, writer, obj);
    }

    protected abstract void RenderView(ViewContext viewContext, TextWriter writer, object instance);
}

 RazorView 

/// <summary>Represents the class used to create views that have Razor syntax.</summary>
public class RazorView : BuildManagerCompiledView
{
    public string LayoutPath { get; private set; }
    public bool RunViewStartPages { get; private set; }
    internal StartPageLookupDelegate StartPageLookup { get; set; }
    internal IVirtualPathFactory VirtualPathFactory { get; set; }
    internal DisplayModeProvider DisplayModeProvider { get; set; }
    public IEnumerable<string> ViewStartFileExtensions { get; private set; }
    public RazorView(ControllerContext controllerContext, string viewPath, string layoutPath, bool runViewStartPages, IEnumerable<string> viewStartFileExtensions) : this(controllerContext, viewPath, layoutPath, runViewStartPages, viewStartFileExtensions, null)
    {
    }
    /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.RazorView" /> class using the view page activator.</summary>
    /// <param name="controllerContext">The controller context.</param>
    /// <param name="viewPath">The view path.</param>
    /// <param name="layoutPath">The layout or master page.</param>
    /// <param name="runViewStartPages">A value that indicates whether view start files should be executed before the view.</param>
    /// <param name="viewStartFileExtensions">The set of extensions that will be used when looking up view start files.</param>
    /// <param name="viewPageActivator">The view page activator.</param>
    public RazorView(ControllerContext controllerContext, string viewPath, string layoutPath, bool runViewStartPages, IEnumerable<string> viewStartFileExtensions, IViewPageActivator viewPageActivator) : base(controllerContext, viewPath, viewPageActivator)
    {
        this.LayoutPath = (layoutPath ?? string.Empty);
        this.RunViewStartPages = runViewStartPages;
        this.StartPageLookup = new StartPageLookupDelegate(StartPage.GetStartPage);
        this.ViewStartFileExtensions = (viewStartFileExtensions ?? Enumerable.Empty<string>());
    }
    /// <summary>Renders the specified view context by using the specified writer and <see cref="T:System.Web.Mvc.WebViewPage" /> instance.</summary>
    /// <param name="viewContext">The view context.</param>
    /// <param name="writer">The writer that is used to render the view to the response.</param>
    /// <param name="instance">The <see cref="T:System.Web.Mvc.WebViewPage" /> instance.</param>
    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, new object[]
            {
                base.ViewPath
            }));
        }
        webViewPage.OverridenLayoutPath = this.LayoutPath;
        webViewPage.set_VirtualPath(base.ViewPath);
        webViewPage.ViewContext = viewContext;
        webViewPage.ViewData = viewContext.ViewData;
        webViewPage.InitHelpers();
        if (this.VirtualPathFactory != null)
        {
            webViewPage.set_VirtualPathFactory(this.VirtualPathFactory);
        }
        if (this.DisplayModeProvider != null)
        {
            webViewPage.set_DisplayModeProvider(this.DisplayModeProvider);
        }
        WebPageRenderingBase webPageRenderingBase = null;
        if (this.RunViewStartPages)
        {
            webPageRenderingBase = this.StartPageLookup(webViewPage, RazorViewEngine.ViewStartFileName, this.ViewStartFileExtensions);
        }
        webViewPage.ExecutePageHierarchy(new WebPageContext(viewContext.HttpContext, null, null), writer, webPageRenderingBase);
    }
}

以IoC的方式激活View

 RazorView 的构造函数中有用到IViewPageActivator,IViewPageActivator旨在View对象的激活。当没有指定一个具体的ViewPageActivator时,默认采用 DefaultViewPageActivator对象。

internal class DefaultViewPageActivator : IViewPageActivator
{
    private Func<IDependencyResolver> _resolverThunk;
    public DefaultViewPageActivator() : this(null)
    {
    }
    public DefaultViewPageActivator(IDependencyResolver resolver)
    {
        if (resolver == null)
        {
            this._resolverThunk = (() => DependencyResolver.Current);
            return;
        }
        this._resolverThunk = (() => resolver);
    }
    public object Create(ControllerContext controllerContext, Type type)
    {
        object result;
        try
        {
            result = (this._resolverThunk().GetService(type) ?? Activator.CreateInstance(type));
        }
        catch (MissingMethodException originalException)
        {
            MissingMethodException ex = TypeHelpers.EnsureDebuggableException(originalException, type.FullName);
            if (ex != null)
            {
                throw ex;
            }
            throw;
        }
        return result;
    }
}

扩展

扩展WebViewPage

 IDependencyResolver 是Asp.net mvc提供的IoC接入口,所以这里可以介入到具体某个WebViewPage<TModel>类型的实例化。

下面的例子是向View中注入一个属性,用于在页面中便捷地读取资源文件信息。

public abstract class LocalizableViewPage<TModel> : WebViewPage<TModel>
{
    [Inject]
    public ResourceReader ResourceReader { get; set; }
}
public class DefaultResourceReader : ResourceReader
{
    public override string GetString(string name)
    {
        return Resources.ResourceManager.GetString(name);
    }
}

在View 顶部 添加命令 @inherits LocalizableViewPage<>

@inherits LocalizableViewPage<object>
<html>
<head>
    <title></title>
</head>
<body>
    <h2>@ResourceReader.GetString("HelloWorld")</h2>
</body>
</html>

或者修改View文件夹下的Web.config配置

<system.web.webPages.razor>
  <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  <pages pageBaseType="LocalizableViewPage">
    <namespaces>
      <add namespace="System.Web.Mvc" />
      <add namespace="System.Web.Mvc.Ajax" />
      <add namespace="System.Web.Mvc.Html" />
      <add namespace="System.Web.Optimization"/>
      <add namespace="System.Web.Routing" />
      <add namespace="MvcApp" />
    </namespaces>
  </pages>
</system.web.webPages.razor>

扩展RazorViewEngine

public class MyViewEngine : RazorViewEngine
{
    public MyViewEngine()
    {
        ViewLocationFormats = new[]
        {
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.cshtml",
            "~/Views/Areas/{1}/{0}.cshtml"
        };
        AreaViewLocationFormats = new[]
        {
            "~/Areas/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/{0}.cshtml",

            "~/Areas/{2}/Views/Config/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/User/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Case/{1}/{0}.cshtml",

            "~/Areas/{2}/Views/Config/Shared/{0}.cshtml",
            "~/Areas/{2}/Views/User/Shared/{0}.cshtml",
            "~/Areas/{2}/Views/Case/Shared/{0}.cshtml"
        };
    }

    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        return base.FindView(controllerContext, viewName, masterName, useCache);
    }
}

ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new MyViewEngine());

  

原文地址:https://www.cnblogs.com/wj033/p/6060699.html