zoukankan      html  css  js  c++  java
  • .NET Core开发日志——视图与页面

    当一个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("
    <p>Hello World!</p>");
      ((RazorPageBase) this).EndContext();
    }
    

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

  • 相关阅读:
    第2课 检索数据
    jmeter断言-响应断言
    jmeter参数化
    性能测试重要指标
    Web测试中,各类web控件测试点(转)
    jmeter-分布式集群测试-执行机无响应数据
    jmeter分布式集群测试-Error initialising remote server
    jmeter分布式集群测试-修改执行机默认端口
    jmeter分布式集群测试
    selenium-webdriver层级定位元素
  • 原文地址:https://www.cnblogs.com/kenwoo/p/9533725.html
Copyright © 2011-2022 走看看