当一个Action完成它的任务后,通常需要返回一个实现IActionResult的对象,而最常见的就是View或者ViewResult,所谓的视图对象。那么视图与最终所看到的页面之间的联系又是怎样形成的,这便是本文想要探讨的问题。

在ResourceInvoker类之中,可以找到下列的代码。这些代码是对返回结果——IActionResult的进一步处理。

case State.ResultInside:
{
... var task = InvokeResultAsync(_result);
if (task.Status != TaskStatus.RanToCompletion)
{
next = State.ResultEnd;
return task;
} goto case State.ResultEnd;
} protected async Task InvokeResultAsync(IActionResult result)
{
var actionContext = _actionContext; _diagnosticSource.BeforeActionResult(actionContext, result);
_logger.BeforeExecutingActionResult(result); try
{
await result.ExecuteResultAsync(actionContext);
}
finally
{
_diagnosticSource.AfterActionResult(actionContext, result);
_logger.AfterExecutingActionResult(result);
}
}

IActionResult接口的实现类ViewResult中会调用ViewResultExecutor类的方法。

public override async Task ExecuteResultAsync(ActionContext context)
{
... var executor = context.HttpContext.RequestServices.GetRequiredService<IActionResultExecutor<ViewResult>>();
await executor.ExecuteAsync(context, this);
}

ViewResultExecutor类里则需要先通过RazorViewEngine类找到对应的视图。

public async Task ExecuteAsync(ActionContext context, ViewResult result)
{
... var viewEngineResult = FindView(context, result);
viewEngineResult.EnsureSuccessful(originalLocations: null); var view = viewEngineResult.View;
using (view as IDisposable)
{ await ExecuteAsync(
context,
view,
result.ViewData,
result.TempData,
result.ContentType,
result.StatusCode);
} ...
}

RazorViewEngine类返回的结果是RazorView对象。注意其内部已包含了IRazorPage对象。

public ViewEngineResult GetView(string executingFilePath, string viewPath, bool isMainPage)
{
... var cacheResult = LocatePageFromPath(executingFilePath, viewPath, isMainPage);
return CreateViewEngineResult(cacheResult, viewPath);
} public ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage)
{
... var cacheResult = LocatePageFromViewLocations(context, viewName, isMainPage);
return CreateViewEngineResult(cacheResult, viewName);
} private ViewEngineResult CreateViewEngineResult(ViewLocationCacheResult result, string viewName)
{
... var page = result.ViewEntry.PageFactory(); var viewStarts = new IRazorPage[result.ViewStartEntries.Count];
for (var i = 0; i < viewStarts.Length; i++)
{
var viewStartItem = result.ViewStartEntries[i];
viewStarts[i] = viewStartItem.PageFactory();
} var view = new RazorView(this, _pageActivator, viewStarts, page, _htmlEncoder, _diagnosticSource);
return ViewEngineResult.Found(viewName, view);
}

找到视图后,ViewResultExecutor再调用其父类ViewExecutor的ExecuteAsync方法。其内部将调用RazorView类的RenderAsync方法。

protected async Task ExecuteAsync(
ViewContext viewContext,
string contentType,
int? statusCode)
{
... var response = viewContext.HttpContext.Response; ResponseContentTypeHelper.ResolveContentTypeAndEncoding(
contentType,
response.ContentType,
DefaultContentType,
out var resolvedContentType,
out var resolvedContentTypeEncoding); response.ContentType = resolvedContentType; if (statusCode != null)
{
response.StatusCode = statusCode.Value;
} using (var writer = WriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
{
var view = viewContext.View; var oldWriter = viewContext.Writer;
try
{
viewContext.Writer = writer; DiagnosticSource.BeforeView(view, viewContext); await view.RenderAsync(viewContext); DiagnosticSource.AfterView(view, viewContext);
}
finally
{
viewContext.Writer = oldWriter;
} // Perf: Invoke FlushAsync to ensure any buffered content is asynchronously written to the underlying
// response asynchronously. In the absence of this line, the buffer gets synchronously written to the
// response as part of the Dispose which has a perf impact.
await writer.FlushAsync();
}
}

RazorView类中可以看到其核心的处理与IRazorPage的ExecuteAsync方法紧密相关。

public virtual async Task RenderAsync(ViewContext context)
{
... _bufferScope = context.HttpContext.RequestServices.GetRequiredService<IViewBufferScope>();
var bodyWriter = await RenderPageAsync(RazorPage, context, invokeViewStarts: true);
await RenderLayoutAsync(context, bodyWriter);
} private async Task<ViewBufferTextWriter> RenderPageAsync(
IRazorPage page,
ViewContext context,
bool invokeViewStarts)
{
var writer = context.Writer as ViewBufferTextWriter;
... // The writer for the body is passed through the ViewContext, allowing things like HtmlHelpers
// and ViewComponents to reference it.
var oldWriter = context.Writer;
var oldFilePath = context.ExecutingFilePath; context.Writer = writer;
context.ExecutingFilePath = page.Path; try
{
if (invokeViewStarts)
{
// Execute view starts using the same context + writer as the page to render.
await RenderViewStartsAsync(context);
} await RenderPageCoreAsync(page, context);
return writer;
}
finally
{
context.Writer = oldWriter;
context.ExecutingFilePath = oldFilePath;
}
} private async Task RenderPageCoreAsync(IRazorPage page, ViewContext context)
{
page.ViewContext = context;
_pageActivator.Activate(page, context); _diagnosticSource.BeforeViewPage(page, context); try
{
await page.ExecuteAsync();
}
finally
{
_diagnosticSource.AfterViewPage(page, context);
}
}

但当查找IRazorPage接口的实现。从RazorPageBaseRazorPage,再到RazorPage<TModel>,这些都只是抽象类,且都没有对ExecuteAsync方法有具体实现。

源码里找不到进一步的实现类,线索到这里断开了。

这时可以建立一个MVC的应用程序,编译后找到它的bin目录,会看到其中包含一个*.View.dll文件。

使用反编译软件,比如dotPeek,查看里面的内容,会找到一些由cshtml文件生成的类。

以其中Views_Home_Index为例,其实际上为RazorPage<TModel>的一个实现类。

它内部的ExecuteAsync方法正是生成页面内容的关键。

因为是VS模板自动生成的页面,上面的代码十分冗杂。为了更清晰地检查核心的代码,不妨减少下页面的复杂度。

把index.cshtml文件内容改成如下:

@{
ViewData["Title"] = "Home Page";
Layout = null;
} <p>Hello World!</p>

再次编译后,可以看到ExecuteAsync方法的内容变成了下面的样子:

public virtual async Task ExecuteAsync()
{
((ViewDataDictionary) this.get_ViewData()).set_Item("Title", (object) "Home Page");
((RazorPageBase) this).set_Layout((string) null);
((RazorPageBase) this).BeginContext(65, 21, true);
((RazorPageBase) this).WriteLiteral("\r\n<p>Hello World!</p>");
((RazorPageBase) this).EndContext();
}

不难看出,最终展现的页面内容便是通过RazorPageBase类的WriteLiteral方法生成的。

.NET Core开发日志——视图与页面的更多相关文章

  1. C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志

    C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...

  2. .NET Core开发日志——Entity Framework与PostgreSQL

    Entity Framework在.NET Core中被命名为Entity Framework Core.虽然一般会用于对SQL Server数据库进行数据操作,但其实它还支持其它数据库,这里就以Po ...

  3. .NET Core开发日志——RequestDelegate

    本文主要是对.NET Core开发日志--Middleware的补遗,但是会从看起来平平无奇的RequestDelegate开始叙述,所以以其作为标题,也是合情合理. RequestDelegate是 ...

  4. .NET Core开发日志——从搭建开发环境开始

    .NET Core自2016年推出1.0版本开始,到目前已是2.1版本,在其roadmap计划里明年更会推出3.0版本,发展不可不谓之迅捷.不少公司在经过一个谨慎的观望期后,也逐步开始将系统升级至最新 ...

  5. .NET Core开发日志——结构化日志

    在.NET生态圈中,最早被广泛使用的日志库可能是派生自Java世界里的Apache log4net.而其后来者,莫过于NLog.Nlog与log4net相比,有一项较显著的优势,它支持结构化日志. 结 ...

  6. .NET Core开发日志——Edge.js

    最近在项目中遇到这样的需求:要将旧有系统的一部分业务逻辑集成到新的自动化流程工具中.这套正在开发的自动化工具使用的是C#语言,而旧有系统的业务逻辑则是使用AngularJS在前端构建而成.所以最初的考 ...

  7. .NET Core开发日志——ADO.NET与SQL Server

    在.NET世界,如果想要对数据库进行操作,总少不了ADO.NET的身影.在.NET Core里同样离不开那些熟悉的类库与API.这里简略地介绍下如何通过ADO.NET对SQL Server进行不同的处 ...

  8. .NET Core开发日志——Linux版本的SQL Server

    SQL Server 2017版本已经可以在Linux系统上安装,但我在尝试.NET Core跨平台开发的时候使用的是Mac系统,所以这里记录了在Mac上安装SQL Server的过程. 最新的SQL ...

  9. .NET Core开发日志——Model Binding

    ASP.NET Core MVC中所提供的Model Binding功能简单但实用,其主要目的是将请求中包含的数据映射到action的方法参数中.这样就避免了开发者像在Web Forms时代那样需要从 ...

随机推荐

  1. git+gitolite如何实现权限控制

    前言 首先说明一下,这还是本人第一次写这类文章,如有不妥,多多见谅. 基本情况 因为现在公司的人不是很多,但是还对代码有着严格的管控,所以采用了gitolite的管理方式 其实正常来讲,这种权限的把控 ...

  2. 高性能Javascript(1)

    第一部分 加载与运行 <html> <head> <title>Script Example</title> </head> <bod ...

  3. sringboot项目在tomcat上的部署

    sringboot项目在tomcat上的部署原文链接: https://blog.csdn.net/zhaoyahui_666/article/details/78283559#comments 20 ...

  4. appium 获取android 粘贴板上的内容

    appium 新版本增加了获取粘贴板的内容.如果使用appium旧版本,获取粘贴板的内容不是那么容易的,甚至百度谷歌各种搜,都无法找到合适的解决方法.新版本获取android 粘贴板内容就显得很容易了 ...

  5. 入门:移动APP中的各种导航

    即使是移动应用界面的原型设计,导航的形式也可以多种多样.尽管尺寸小,又必须紧凑排列大量数据,它们似乎受到了紧密的约束,但依然有着形形色色的选择. 人们曾经一度只会考虑一种形式——流行且广泛使用的垂直导 ...

  6. [svc]gns3模拟器及探讨几个bgp问题

    模拟器 链接:https://pan.baidu.com/s/1geMcmND 密码:7iir gns0.8.6的版本好用 思科的这个iso好用: c3660-js2-mz.124-21a.bin C ...

  7. linux每日命令(33):diff命令

    diff 命令是 linux上非常重要的工具,用于比较文件的内容,特别是比较两个版本不同的文件以找到改动的地方.diff在命令行中打印每一个行的改动.最新版本的diff还支持二进制文件.diff程序的 ...

  8. 【iCore4 双核心板_ARM】例程二十一:LWIP_TCP_SERVER实验——以太网数据传输

    实验现象: 核心代码: int main(void) { system_clock.initialize(); led.initialize(); adc.initialize(); delay.in ...

  9. 【GMT43智能液晶模块】例程四:SYSTICK定时器——定时读取触摸值

    实验原理: 本实验采用系统定时器,通过简单的初始化定时20ms,每20ms读取一次触 摸值,并基于emWin的UI界面将读到的触摸值显示在界面上. 实验现象: 源代码下载链接: 链接:http://p ...

  10. Mysql系列七:分库分表技术难题之分布式全局唯一id解决方案

    一.前言 在前面的文章Mysql系列四:数据库分库分表基础理论中,已经说过分库分表需要应对的技术难题有如下几个: 1. 分布式全局唯一id 2. 分片规则和策略 3. 跨分片技术问题 4. 跨分片事物 ...