Render Razor Просмотр строки в ASP.NET Core
Я использую RazorEngine для синтаксического анализа шаблонов в моем проекте MVC 6 следующим образом:
Engine.Razor.RunCompile(File.ReadAllText(fullTemplateFilePath), templateName, null, model);
Он отлично работает для бета-версии 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"
}
}
Это 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>
Ответы
Ответ 1
ОБНОВЛЕНИЕ Июль 2016 года
Работает отлично в следующих версиях 1.0.0
, RC2
Кто ориентируется на aspnetcore RC2, этот фрагмент может помочь вам:
- Создайте отдельную службу, чтобы ее можно было использовать, если вы не находитесь в контексте контроллера, например. из командной строки или в очередь очереди и т.д.
- Зарегистрируйте эту службу в своем контейнере IoC в классе
Startup
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.
Кредит
Источник извлекается из (спасибо To @pholly): https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs)
Ответ 2
В прошлом Ive использовал RazorEngine
внутри Библиотеки классов, потому что моя цель состояла в том, чтобы отображать шаблоны из этой библиотеки классов.
Насколько я понимаю, вы, кажется, внутри проекта MVC 6.0, поэтому почему бы не использовать метод RenderPartialViewToString()
, не добавляя зависимость от RazorEngine
?
Имейте в виду, я только спрашиваю, потому что мне любопытно.
Например, из VS2015 я создал новое веб-приложение ASP.NET и выбрал шаблон веб-приложения из шаблонов предварительного просмотра ASP.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
я создал новую Шаблоны, в которой Ive добавил простой RegistrationTemplate.cshtml
View строго типизированный в мой 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
, но идея состоит в том, чтобы показать, как она используется.
В этот момент я мог бы (например) вызвать inv EmailService
и передать emailbody
:
_emailService.SendEmailAsync("[email protected]", "registration", emailbody);
Я не уверен, что это подходящая альтернатива для вашей текущей задачи.
Ответ 3
Я нашел эту тему, которая обсуждает ее: 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;
}
}
}
Ответ 4
Сегодня я закончил с моей библиотекой, которая может решить вашу проблему. Вы можете использовать его из 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
Ответ 5
ResolveUrlMethodName
удален. Поэтому в CreateHost
здесь вы пытаетесь установить свойство, которое не существует:).
Мы решили перенести обработку ~/
с основного Razor на a TagHelper
, реализованную в сборке Microsoft.AspNet.Mvc.Razor
. Здесь commit к битам, которые удалили этот метод.
Надеюсь, это поможет.
Ответ 6
Чтобы улучшить на @vlince ответ (который не работал из коробки для меня), вот что я сделал:
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 TutorialController(ICompositeViewEngine viewEngine) : base(viewEngine) { }
public string Index(int? id)
{
var model = new MyModel { Name = "My Name" };
return RenderViewAsString(model);
}
}
}
Ответ 7
Метод расширения для преобразования частичных представлений в ответ строки.
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)
}
Ответ 8
Альтернативное решение, использующее только ASP.NET Core, без внешних библиотек и без отражения можно найти здесь: https://weblogs.asp.net/ricardoperes/getting-html-for-a-viewresult-in-asp-net-core.
Для этого требуется только ViewResult и HttpContext.