在ASP.NET 5中如何将Razor视图呈现为字符串?

16

在之前的ASP.NET版本中,虽然不是很简单,但是可以将Razor视图呈现为字符串。我看到的方法是使用假控制器,或者使用一些外部引擎,如RazorEngine

现在,在ASP.NET 5中有很多改变,我想知道是否比以前更简单了。所以在新版本的框架中,是否有一种直接的方法来将Razor视图呈现为字符串,还是我们仍然需要使用以前版本中的方法?


你想将一个操作的HTML内容插入到字符串变量中吗?还是只是在另一个视图中呈现ActionResult? - Fabio Luz
@FabioLuz 我想渲染视图并将渲染后的 HTML 代码放入一个字符串变量中。 - user1620696
如果您想在MVC 6下执行此操作,那么应该非常简单。这只是一个获取正确依赖项以渲染Razor视图的问题。但是,如果您不在其中,则可以参考以下示例:https://github.com/tugberkugurlu/RazorOnConsole/blob/34c2e308f1976680c654a06a066b6c7fda1387b6/RazorTemplatingSample.Console/Program.cs#L15 深入了解如何实现“Templater.Run”。 - tugberk
1
@tugberk,那个链接很棒,谢谢!虽然我不是原帖作者,但你的代码库非常有帮助。 - Will
2个回答

20

我使用从IServiceProvider注入的以下类型:

ICompositeViewEngine viewEngine;
ITempDataProvider tempDataProvider;
IHttpContextAccessor httpContextAccessor;

我使用以下方法呈现内容:

private async Task<string> RenderView(string path, ViewDataDictionary viewDataDictionary, ActionContext actionContext)
{
    using (var sw = new System.IO.StringWriter())
    {
        var viewResult = viewEngine.FindView(actionContext, path);

        var viewContext = new ViewContext(actionContext, viewResult.View, viewDataDictionary, new TempDataDictionary(httpContextAccessor, tempDataProvider), sw);

        await viewResult.View.RenderAsync(viewContext);
        sw.Flush();

        if (viewContext.ViewData != viewDataDictionary)
        {
            var keys = viewContext.ViewData.Keys.ToArray();
            foreach (var key in keys)
            {
                viewDataDictionary[key] = viewContext.ViewData[key];
            }
        }

        return sw.ToString();
    }
}

我把它叫做这个:

var path = "~/Views/Home/Index.cshtml";
var viewDataDictionary = new ViewDataDictionary(new Microsoft.AspNet.Mvc.ModelBinding.EmptyModelMetadataProvider(), new Microsoft.AspNet.Mvc.ModelBinding.ModelStateDictionary());
var actionContext = new ActionContext(httpContextAccessor.HttpContext, new Microsoft.AspNet.Routing.RouteData(), new ActionDescriptor());
viewDataDictionary.Model = null;
var text = await RenderView(path, viewDataDictionary, actionContext);
当然,我的viewDataDictionaryactionContext变量是由另一个方法设置的,以实现封装。如果您选择修改new ViewDataDictionary行,可能会导致类型化模型绑定到您的视图。此代码使用了大量的using语句,我认为我已经在下面列出了它们。否则,VS2015可以很好地找到它们。
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;

这篇文章是在beta-3下撰写的;它仍然可以构建,但有些东西可能会发生变化。如果有变化,我会尽力回来更新这里。


很高兴能帮忙!感谢您指出它仍然有效 - 我之前没有去修改它,所以也没有在我的答案中添加那个注释。 - Matt DeKrey
1
没有看到你是如何获取那些注入的对象的,而且方法的签名与你调用它的方式不一致... - Shockwaver
1
@Shockwaver - 以防有人想知道,这只是依赖注入(DI)。服务依赖通常是类构造函数的参数,而DI容器会确定从哪里获取它们。 关于签名不一致的问题,Matt可能在控制器内编写了原始方法,因此您可以将其更改为大写A并从控制器内开始测试代码。 - Seth

4

我曾在一年前使用过一种解决方案。我不确定是否有新的/更好的方法来解决问题,但它可以解决问题。

public string RenderViewToString(string viewName, object model)
{
    ViewData.Model = model;
    using (var sw = new StringWriter())
    {
       var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
       var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
       viewResult.View.Render(viewContext, sw);
       viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
       return sw.GetStringBuilder().ToString();
     }
}

3
很遗憾,ViewEngines类在asp.net-5中也不存在。 - Matt DeKrey
1
@MattDeKrey 但是在v4中确实可以(我知道这不是OP的要求)!而且这绝对是该版本的正确方法!太棒了,救了我的一天,使用razor的任何html助手都没有问题。谢谢你! - Shockwaver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接