在ASP中将Razor视图渲染为字符串.净的核心
本文关键字:字符串 核心 ASP 中将 Razor 视图 | 更新日期: 2023-09-27 18:12:40
我使用RazorEngine在我的MVC 6项目中解析模板,如下所示:
Engine.Razor.RunCompile(File.ReadAllText(fullTemplateFilePath), templateName, null, model);
它在beta 6中工作得很好。升级到beta 7后无法工作,错误如下:
MissingMethodException: Method not found: "Void microsoft.aspnet . razor . codegenerator . generatedclasscontext . set_resolveurlmethodname (System.String)"。在RazorEngine.Compilation.CompilerServiceBase。CreateHost(Type templateType, Type modelType, String className)
This is 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>
有人有类似的问题吗?有另一种方法来解析模板在MVC 6?
更新7月,2016年
在以下版本1.0.0
, RC2
上运行良好
谁的目标是aspnetcore RC2,这个片段可能会帮助你:
- 创建一个单独的Service,这样你就可以使用它,如果你不是在一个控制器上下文中,例如从命令行或队列运行器,等等…
- 在
Startup
类的IoC容器中注册此服务
// 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)
我发现这个线程讨论它: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>();
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;
}
}
}
在过去,我在类库中使用RazorEngine
,因为我的目标是从这个类库中呈现模板。
从我的理解,你似乎是在MVC 6.0项目内,所以为什么不使用RenderPartialViewToString()
方法,而不必添加对RazorEngine
的依赖?
请记住,我问这个只是因为我很好奇。
例如,在VS2015中,我创建了一个新的ASP。. NET Web应用程序,并从ASP. NET中选择Web应用程序模板。. NET 5预览模板。
在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);
}
}
}
然后我创建了一个新的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
文件夹中,我创建了一个新的模板文件夹,在其中我添加了一个简单的RegistrationTemplate.cshtml
视图强类型到我的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>
最后一步是让我的Controller
继承我的BaseController
public class MyController : BaseController
并创建如下内容:
public IActionResult Index()
{
var model = new PersonViewModel();
model.FirstName = "Frank";
model.LastName = "Underwood";
var emailbody = base.RenderPartialViewToString("Templates/RegistrationTemplate", model);
return View();
}
当然,上面的例子是无用的,因为我没有对变量emailbody
做任何操作,但是我的想法是展示它是如何使用的。
此时,我可以(例如)调用EmailService
并传递emailbody
:
_emailService.SendEmailAsync("test@test.com", "registration", emailbody);
我不确定这是否适合你目前的任务。
今天我已经完成了我的库,可以解决你的问题。你可以在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
为了改进@ vince答案(这对我来说不是开箱即用的),以下是我所做的:
1-创建一个基本控制器,你的其他控制器将继承
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-继承基本控制器并调用方法
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewEngines;
namespace YourNameSpace
{
public class YourController : BaseController
{
public YourController(ICompositeViewEngine viewEngine) : base(viewEngine) { }
public string Index(int? id)
{
var model = new MyModel { Name = "My Name" };
return RenderViewAsString(model);
}
}
}
ResolveUrlMethodName
被删除。因此,在您的CreateHost
这里,您试图设置一个不存在的属性:)。
我们决定将~/
处理从核心Razor移动到Microsoft.AspNet.Mvc.Razor
组件中实现的TagHelper
。这是提交到删除方法的位。
希望这有帮助。
将部分视图转换为字符串响应的扩展方法。
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)
}
。. NET 5实现
public static async Task<string> ViewToString(this PartialViewResult partialView, Controller controller)
{
using (var writer = new StringWriter())
{
var services = controller.ControllerContext.HttpContext.RequestServices;
var viewEngine = services.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
var viewName = partialView.ViewName ?? controller.ControllerContext.ActionDescriptor.ActionName;
var view = viewEngine.FindView(controller.ControllerContext, viewName, false).View;
var viewContext = new ViewContext(controller.ControllerContext, view, partialView.ViewData, partialView.TempData, writer, new HtmlHelperOptions());
await view.RenderAsync(viewContext);
return writer.ToString();
}
}
仅使用ASP的另一种解决方案。. NET Core,没有外部库,也没有反射可以在这里找到:https://weblogs.asp.net/ricardoperes/getting-html-for-a-viewresult-in-asp-net-core。只需要一个ViewResult
和一个HttpContext
。
这个想法是拿起一个ViewResult
并调用一些方法,比如ToHtml
,来获得渲染的输出。这个方法看起来像这样:
public static class ViewResultExtensions {
public static string ToHtml(this ViewResult result, HttpContext httpContext) {
var feature = httpContext.Features.Get<IRoutingFeature>();
var routeData = feature.RouteData;
var viewName = result.ViewName ?? routeData.Values["action"] as string;
var actionContext = new ActionContext(httpContext, routeData, new ControllerActionDescriptor());
var options = httpContext.RequestServices.GetRequiredService<IOptions<MvcViewOptions>>();
var htmlHelperOptions = options.Value.HtmlHelperOptions;
var viewEngineResult = result.ViewEngine?.FindView(actionContext, viewName, true) ?? options.Value.ViewEngines.Select(x => x.FindView(actionContext, viewName, true)).FirstOrDefault(x => x != null);
var view = viewEngineResult.View;
var builder = new StringBuilder();
using (var output = new StringWriter(builder)) {
var viewContext = new ViewContext(actionContext, view, result.ViewData, result.TempData, output, htmlHelperOptions);
view
.RenderAsync(viewContext)
.GetAwaiter()
.GetResult();
}
return builder.ToString();
}
}
使用它,只需:
var view = this.View(“ViewName”);
var html = view.ToHtml();