在ASP.NET Core中将Razor View渲染为string

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

Engine.Razor.RunCompile(File.ReadAllText(fullTemplateFilePath), templateName, null, model); 

它工作正常的beta6。升级到beta7后,错误:

MissingMethodException:Method not found:“Void Microsoft.AspNet.Razor.CodeGenerators.GeneratedClassContext.set_ResolveUrlMethodName(System.String)”。 在RazorEngine.Compilation.CompilerServiceBase.CreateHost(Type templateType,Type modelType,String className)

这是global.json:

 { "projects": [ "src", "test" ], "sdk": { "version": "1.0.0-beta7", "runtime": "clr", "architecture": "x64" } } 

这是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": { } }, ... 

我的模板是:

 @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> 

有没有人有类似的问题? 还有另一种方法来parsingMVC 6中的模板?

更新2016年7月

在以下版本1.0.0RC2


谁瞄准aspnetcore RC2,这个片段可能会帮助你:

  • 创build一个单独的服务,所以你可以使用它,如果你不在控制器上下文中,例如从命令行或者队列运行器等等。
  • 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); 

笔记

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

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

信用

来源是从(谢谢@pholly)提取: https : //github.com/aspnet/Entropy/blob/dev/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs )

在过去,我在类库中使用了RazorEngine因为我的目标是从这个类库中呈现模板。

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

请记住,我只是问,因为我好奇。

出于示例目的,在VS2015中,我创build了一个新的ASP.NET Web应用程序,并从ASP.NET 5预览模板中select了Web应用程序模板。

ViewModels文件夹中,我创build了一个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); } } } 

然后我创build了一个新的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(); } } 

信用去@DavidG为他的方法

Views-->Shared文件夹中,我创build了一个新的Templates文件夹,我在其中添加了一个简单的RegistrationTemplate.cshtml View,这个PersonViewModel typesPersonViewModel如下所示:

 @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> 

最后一步是让我的Controller从我的BaseControllerinheritance

 public class MyController : BaseController 

并创build如下所示:

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

当然,上面的例子是没用的,因为我对variablesemailbody什么都不做,但是想法是显示它是如何使用的。

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

 _emailService.SendEmailAsync("test@test.com", "registration", emailbody); 

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

我发现这个讨论它的线程: https : //github.com/aspnet/Mvc/issues/3091

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

经过试验和错误,我能够修剪服务,所以它只需要一个有效的HttpContextViewEngine ,我添加了一个不需要模型的重载。 视图是相对于您的应用程序根(他们不必住在一个Views文件夹)。

您需要在Startup.cs注册服务,并注册HttpContextAccessor

 //Startup.cs ConfigureServices() services.AddTransient<ViewRenderService>(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); 
 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(); } } } } 

用法示例:

 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; } } } 

今天我已经完成了我的图书馆,可以解决您的问题。 您可以使用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 

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

ResolveUrlMethodName已被删除。 因此,在你的CreateHost ,你试图设置一个不存在的属性:)。

我们决定将核心Razor中的~/处理转换为在Microsoft.AspNet.Mvc.Razor程序TagHelper实现的TagHelper 。 以下是删除该方法的位的提交 。

希望这有助于。

为了改进@vlince的答案(这不是开箱即用的),这是我做的:

1-创build您的其他控制器将inheritance的基础控制器

 using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Mvc.Rendering; using System.IO; namespace YourNameSpace { public class BaseController : Controller { protected ICompositeViewEngine viewEngine; public BaseController(ICompositeViewEngine viewEngine) { this.viewEngine = viewEngine; } protected string RenderViewAsString(object model, string viewName = null) { viewName = viewName ?? ControllerContext.ActionDescriptor.ActionName; ViewData.Model = model; using (StringWriter sw = new StringWriter()) { IView view = viewEngine.FindView(ControllerContext, viewName, true).View; ViewContext viewContext = new ViewContext(ControllerContext, view, ViewData, TempData, sw, new HtmlHelperOptions()); view.RenderAsync(viewContext).Wait(); return sw.GetStringBuilder().ToString(); } } } } 

2-inheritance基础控制器并调用方法

 using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ViewEngines; namespace YourNameSpace { public class YourController : BaseController { public TutorialController(ICompositeViewEngine viewEngine) : base(viewEngine) { } public string Index(int? id) { var model = new MyModel { Name = "My Name" }; return RenderViewAsString(model); } } } 

将部分视图转换为string响应的扩展方法。

 public static class PartialViewToString { public static async Task<string> ToString(this PartialViewResult partialView, ActionContext actionContext) { using(var writer = new StringWriter()) { var services = actionContext.HttpContext.RequestServices; var executor = services.GetRequiredService<PartialViewResultExecutor>(); var view = executor.FindView(actionContext, partialView).View; var viewContext = new ViewContext(actionContext, view, partialView.ViewData, partialView.TempData, writer, new HtmlHelperOptions()); await view.RenderAsync(viewContext); return writer.ToString(); } } } 

在您的控制器操作中的用法

 public async Task<IActionResult> Index() { return await PartialView().ToString(ControllerContext) } 

另一个只使用ASP.NET Core的解决scheme,没有外部库,也没有reflection可以在这里find: https : //weblogs.asp.net/ricardoperes/getting-html-for-a-viewresult-in-asp-net-核心 。 它只需要一个ViewResult和一个HttpContext。