在ASP.NET Core中将Razor View渲染为字符串

hcp*_*hcp 28 c# razor asp.net-core-mvc asp.net-core

我使用RazorEngine解析我的MVC 6项目中的模板,如下所示:

Engine.Razor.RunCompile(File.ReadAllText(fullTemplateFilePath), templateName, null, model);
Run Code Online (Sandbox Code Playgroud)

它适用于测试版6.它在升级到测试版7后出现错误:

MissingMethodException:找不到方法:"Void Microsoft.AspNet.Razor.CodeGenerators.GeneratedClassContext.set_ResolveUrlMethodName(System.String)".在RazorEngine.Compilation.CompilerServiceBase.CreateHost中(类型templateType,类型modelType,String className)

这是global.json:

{
  "projects": [ "src", "test" ],
  "sdk": {
    "version": "1.0.0-beta7",
    "runtime": "clr",
    "architecture": "x64"
  }
}
Run Code Online (Sandbox Code Playgroud)

这是project.json:

...
"dependencies": {
    "EntityFramework.SqlServer": "7.0.0-beta7",
    "EntityFramework.Commands": "7.0.0-beta7",
    "Microsoft.AspNet.Mvc": "6.0.0-beta7",
    "Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-beta7",
    "Microsoft.AspNet.Authentication.Cookies": "1.0.0-beta7",
    "Microsoft.AspNet.Authentication.Facebook": "1.0.0-beta7",
    "Microsoft.AspNet.Authentication.Google": "1.0.0-beta7",
    "Microsoft.AspNet.Authentication.MicrosoftAccount": "1.0.0-beta7",
    "Microsoft.AspNet.Authentication.Twitter": "1.0.0-beta7",
    "Microsoft.AspNet.Diagnostics": "1.0.0-beta7",
    "Microsoft.AspNet.Diagnostics.Entity": "7.0.0-beta7",
    "Microsoft.AspNet.Identity.EntityFramework": "3.0.0-beta7",
    "Microsoft.AspNet.Server.IIS": "1.0.0-beta7",
    "Microsoft.AspNet.Server.WebListener": "1.0.0-beta7",
    "Microsoft.AspNet.StaticFiles": "1.0.0-beta7",
    "Microsoft.AspNet.Tooling.Razor": "1.0.0-beta7",
    "Microsoft.Framework.Configuration.Abstractions": "1.0.0-beta7",
    "Microsoft.Framework.Configuration.Json": "1.0.0-beta7",
    "Microsoft.Framework.Configuration.UserSecrets": "1.0.0-beta7",
    "Microsoft.Framework.Logging": "1.0.0-beta7",
    "Microsoft.Framework.Logging.Console": "1.0.0-beta7",
    "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-beta7",
    "RazorEngine": "4.2.2-beta1"
  },
...
  "frameworks": {
    "dnx451": { }
  },
...
Run Code Online (Sandbox Code Playgroud)

我的模板是:

@model dynamic
@{
    Layout = null;
}

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>Registration</title>
</head>
<body>
<p>
    Hello, @Model
</p>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

有没有人有类似的问题?还有另一种解析MVC 6模板的方法吗?

amd*_*amd 26

2016年7月更新

在以下版本中正常工作1.0.0,RC2


谁的目标是aspnetcore RC2,这个代码片段可能对您有所帮助:

  • 创建一个单独的服务,因此如果您不在控制器上下文中,例如从命令行或队列运行程序等,您可以使用它...
  • Startup课堂上的 IoC容器中注册此服务

https://gist.github.com/ahmad-moussawi/1643d703c11699a6a4046e57247b4d09

用法

// using a Model
string html = view.Render("Emails/Test", new Product("Apple"));

// using a Dictionary<string, object>
var viewData = new Dictionary<string, object>();
viewData["Name"] = "123456";

string html = view.Render("Emails/Test", viewData);
Run Code Online (Sandbox Code Playgroud)

笔记

Razor中的链接呈现为相对 URL,因此这不适用于外部视图(如电子邮件等).

至于现在我在控制器上生成链接并通过ViewModel将其传递给视图.

信用

源代码摘自(感谢@pholly):https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs)

  • Gist与https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs完全相同,因为https://github.com/aspnet/Mvc/issues/ 3091 (6认同)

pho*_*lly 19

我找到了这个讨论它的线程:https://github.com/aspnet/Mvc/issues/3091

线程中有人在这里创建了一个示例服务:https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs

经过反复试验后,我能够减少服务,因此它只需要一个有效的HttpContext和一个,ViewEngine并且我添加了一个不需要模型的重载.视图与应用程序根目录相关(它们不必存在于Views文件夹中).

您需要注册该服务Startup.cs并注册HttpContextAccessor:

//Startup.cs ConfigureServices()
services.AddTransient<ViewRenderService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Run Code Online (Sandbox Code Playgroud)
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System;
using System.IO;

namespace LibraryApi.Services
{
    public class ViewRenderService
    {
        IRazorViewEngine _viewEngine;
        IHttpContextAccessor _httpContextAccessor;

        public ViewRenderService(IRazorViewEngine viewEngine, IHttpContextAccessor httpContextAccessor)
        {
            _viewEngine = viewEngine;
            _httpContextAccessor = httpContextAccessor;
        }

        public string Render(string viewPath)
        {
            return Render(viewPath, string.Empty);
        }

        public string Render<TModel>(string viewPath, TModel model)
        {
            var viewEngineResult = _viewEngine.GetView("~/", viewPath, false);

            if (!viewEngineResult.Success)
            {
                throw new InvalidOperationException($"Couldn't find view {viewPath}");
            }

            var view = viewEngineResult.View;

            using (var output = new StringWriter())
            {
                var viewContext = new ViewContext();
                viewContext.HttpContext = _httpContextAccessor.HttpContext;
                viewContext.ViewData = new ViewDataDictionary<TModel>(new EmptyModelMetadataProvider(), new ModelStateDictionary())
                { Model = model };
                viewContext.Writer = output;

                view.RenderAsync(viewContext).GetAwaiter().GetResult();

                return output.ToString();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using LibraryApi.Services;
using System.Dynamic;

namespace LibraryApi.Controllers
{
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        ILogger<ValuesController> _logger;
        ViewRenderService _viewRender;
        public ValuesController(ILogger<ValuesController> logger, ViewRenderService viewRender)
        {
            _logger = logger;
            _viewRender = viewRender;
        }

        // GET api/values
        [HttpGet]
        public string Get()
        {
            //ViewModel is of type dynamic - just for testing
            dynamic x = new ExpandoObject();
            x.Test = "Yes";
            var viewWithViewModel = _viewRender.Render("eNotify/Confirm.cshtml", x);
            var viewWithoutViewModel = _viewRender.Render("MyFeature/Test.cshtml");
            return viewWithViewModel + viewWithoutViewModel;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Vli*_*nce 15

在过去,我使用RazorEngine了类库中的内部,因为我的目标是从此类库中呈现模板.

根据我的理解,你似乎是在一个MVC 6.0项目中,所以为什么不使用一个RenderPartialViewToString()方法而不必添加依赖RazorEngine

请记住,我只是在问,因为我很好奇.

例如,在VS2015中,我创建了一个新的ASP.NET Web应用程序,并从ASP.NET 5预览模板中选择了Web应用程序模板.

ViewModels文件夹里面,我创建了一个PersonViewModel:

public class PersonViewModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", this.FirstName, this.LastName);
        }
    } 
}
Run Code Online (Sandbox Code Playgroud)

然后我创建了一个新的BaseController并添加了一个RenderPartialViewToString()方法:

public string RenderPartialViewToString(string viewName, object model)
{
    if (string.IsNullOrEmpty(viewName))
        viewName = ActionContext.ActionDescriptor.Name;

    ViewData.Model = model;

    using (StringWriter sw = new StringWriter())
    {
        var engine = Resolver.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
        ViewEngineResult viewResult = engine.FindPartialView(ActionContext, viewName);

        ViewContext viewContext = new ViewContext(ActionContext, viewResult.View, ViewData, TempData, sw,new HtmlHelperOptions());

        var t = viewResult.View.RenderAsync(viewContext);
        t.Wait();

        return sw.GetStringBuilder().ToString();
    }
}
Run Code Online (Sandbox Code Playgroud)

感谢@DavidG的方法

Views-->Shared文件夹中,我创建了一个新的Templates文件夹,其中我添加了一个简单的RegistrationTemplate.cshtmlView 强类型到我PersonViewModel这样:

@model MyWebProject.ViewModels.PersonViewModel
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>Registration</title>
</head>
<body>
    <p>
        Hello, @Model.FullName
    </p>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

最后一步是让我Controller继承我的BaseController

public class MyController : BaseController
Run Code Online (Sandbox Code Playgroud)

并创建类似于:

public IActionResult Index()
{
    var model = new PersonViewModel();
    model.FirstName = "Frank";
    model.LastName = "Underwood";
    var emailbody = base.RenderPartialViewToString("Templates/RegistrationTemplate", model);

    return View();
}
Run Code Online (Sandbox Code Playgroud)

当然,上面的例子是无用的,因为我对变量什么都不做,emailbody但想法是显示它是如何使用的.

在这一点上,我可以(例如),调用EmailService并传递emailbody:

_emailService.SendEmailAsync("test@test.com", "registration", emailbody);
Run Code Online (Sandbox Code Playgroud)

我不确定这是否适合您当前的任务.

  • 如何转换此代码以使用.NET CORE? (4认同)

Tod*_*ams 12

今天我已经完成了我的图书馆,可以解决你的问题.您可以在ASP.NET中使用它,因为它没有依赖它

例:

string content = "Hello @Model.Name. Welcome to @Model.Title repository";

var model = new
{
  Name = "John Doe",
  Title = "RazorLight"
};

var engine = new RazorLightEngine();
string result = engine.ParseString(content, model);

//Output: Hello John Doe, Welcome to RazorLight repository
Run Code Online (Sandbox Code Playgroud)

更多:https://github.com/toddams/RazorLight

  • 感谢您使用RazorLight库.只要您解析单个视图文件,此处提供的其他一些解决方案就可以正常工作.当我需要支持_ViewStart和_Layout文件时,我切换到了您的库.你的lib非常适合我的需求. (2认同)