当前位置: 移动技术网 > IT编程>开发语言>.net > .NET Core开发日志——视图与页面

.NET Core开发日志——视图与页面

2018年08月26日  | 移动技术网IT编程  | 我要评论

寸芒后传,寻仙仲夏竹马,大兵小将好看吗

当一个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方法生成的。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网