Проблемы с RazorEngine с @Html
Я использую RazorEngine для визуализации некоторого базового контента (очень грубой системы управления контентом).
Он отлично работает, пока я не включу какой-либо синтаксис @Html в разметку.
Если разметка содержит @html, я получаю следующую ошибку:
Невозможно скомпилировать шаблон. Имя "Html" не существует в текущий контекст
Это представление, которое отображает разметку:
@Model Models.ContentPage
@{
ViewBag.Title = Model.MetaTitle;
Layout = "~/Views/Shared/Templates/_" + Model.Layout + "Layout.cshtml";
}
@Html.Raw(RazorEngine.Razor.Parse(Model.Markup, Model))
Я видел на сайте Codeplex для RazorEngine использование @Html (я знаю, что версия устарела и Я получил свою версию через nuget).
Любая помощь по этому поводу была бы большой.
Ответы
Ответ 1
Свойства-помощники Html
и Url
являются актуальными функциями реализации MVC Razor в их механизмах просмотра. Из коробки Html
и Url
в настоящее время не поддерживаются без специализированного базового шаблона.
Предстоящая версия v3 будет сопровождаться соответствующим выпуском RazorEngine.Web, который, мы надеемся, будет содержать базовый шаблон, совместимый с MVC3, с поддержкой Html
и Url
.
Пример, который я написал на главной странице проекта, является просто примером использования настраиваемого базового шаблона.
Вы можете узнать больше о v3 на https://github.com/Antaris/RazorEngine
Ответ 2
Откроется страница https://github.com/Antaris/RazorEngine/wiki/6.-Encoding-Values. Я копирую/миную его здесь:
По умолчанию RazorEngine настроен на кодирование как HTML. Иногда это создает проблемы, когда определенные символы кодируются как HTML, когда вы хотите, чтобы результат был как есть.
Чтобы вывести что-то в необработанном формате, используйте встроенный метод @Raw(), как показано в следующем примере:
string template = "@Raw(Model.Data)";
var model = new { Data = "My raw double quotes appears here \"hello!\"" };
string result = Razor.Parse(template, model);
Это должно привести к:
My raw double quotes appears here "hello!"
Ответ 3
Это довольно старый вопрос, но я нашел хороший ответ на coderwall. Решение состоит в том, чтобы использовать:
@(new RawString("<strong>Bold!</strong>"))
или просто:
@(new RawString(Model.YourHTMLStrinInModel))
Надеюсь, это поможет.
Ответ 4
Это больше года, но поскольку я не нашел рабочую копию в любом месте в Интернете, а страница github неактивна, я решил поделиться своей реализацией с добавлением синтаксиса @Html helper для RazorEngine. Вот реализация, с которой я столкнулся, используя реализацию Abu Haider в качестве отправной точки.
Предоставлено комментарий miketrash: если вы пытаетесь использовать @Html.Action(), вам нужно будет добавить RequestContext (вы можете использовать HttpContext.Current.Request.RequestContext
). Я не включал контекст запроса, потому что он не всегда доступен для моего приложения.
[RequireNamespaces("System.Web.Mvc.Html")]
public class HtmlTemplateBase<T>:TemplateBase<T>, IViewDataContainer
{
private HtmlHelper<T> helper = null;
private ViewDataDictionary viewdata = null;
public HtmlHelper<T> Html
{
get
{
if (helper == null)
{
var writer = this.CurrentWriter; //TemplateBase.CurrentWriter
var vcontext = new ViewContext() { Writer = writer, ViewData = this.ViewData};
helper = new HtmlHelper<T>(vcontext, this);
}
return helper;
}
}
public ViewDataDictionary ViewData
{
get
{
if (viewdata == null)
{
viewdata = new ViewDataDictionary();
viewdata.TemplateInfo = new TemplateInfo() { HtmlFieldPrefix = string.Empty };
if (this.Model != null)
{
viewdata.Model = Model;
}
}
return viewdata;
}
set
{
viewdata = value;
}
}
public override void WriteTo(TextWriter writer, object value)
{
if (writer == null)
throw new ArgumentNullException("writer");
if (value == null) return;
//try to cast to RazorEngine IEncodedString
var encodedString = value as IEncodedString;
if (encodedString != null)
{
writer.Write(encodedString);
}
else
{
//try to cast to IHtmlString (Could be returned by Mvc Html helper methods)
var htmlString = value as IHtmlString;
if (htmlString != null) writer.Write(htmlString.ToHtmlString());
else
{
//default implementation is to convert to RazorEngine encoded string
encodedString = TemplateService.EncodedStringFactory.CreateEncodedString(value);
writer.Write(encodedString);
}
}
}
}
Мне также пришлось переопределить метод WriteTo
TemplateBase
, потому что иначе RazorEngine будет html-кодировать результат вспомогательного метода, означая, что вы избежите "<", " > " и кавычек (см. этот вопрос). Переопределение добавляет проверку для значения IHtmlString
, прежде чем прибегать к выполнению кодирования.
Ответ 5
Мои извинения, у меня нет требуемой 50 репутации, чтобы добавить комментарий, чтобы ответить.
Если кто-то задается вопросом (поскольку JamesStuddart был), метод SetTemplateBase() отсутствует, но вы можете создать экземпляр конфигурации для инициализации службы с помощью базового шаблона.
От http://razorengine.codeplex.com/discussions/285937 Я адаптировал свой код, чтобы он выглядел так:
var config = new RazorEngine.Configuration.TemplateServiceConfiguration
{
BaseTemplateType = typeof(MyHtmlTemplateBase<>)
};
using (var service = new RazorEngine.Templating.TemplateService(config))
{
// Use template service.
Razor.SetTemplateService(service);
result = Razor.Parse(templateString, model);
}
Ответ 6
Html.Raw самое простое решение !! Необходимо 3 шага
Шаг 1: Наследовать от TemplateBase:
public class HtmlSupportTemplateBase<T> : TemplateBase<T>
{
public HtmlSupportTemplateBase()
{
Html = new MyHtmlHelper();
}
public MyHtmlHelper Html { get; set; }
}
Шаг 2: Создайте объект, который делает доступными все методы HTML, используемые вашим шаблоном. В этом примере Html.Raw и Html.Encode становятся доступными в cshtml. шаблон
public class MyHtmlHelper
{
/// <summary>
/// Instructs razor to render a string without applying html encoding.
/// </summary>
/// <param name="htmlString"></param>
/// <returns></returns>
public IEncodedString Raw(string htmlString)
{
return new RawString(htmlString);
}
public string Encode(string value)
{
return System.Net.WebUtility.HtmlEncode(value);
}
public string Encode(object value)
{
return "do whatever";
}
}
Шаг 3:
var config = new TemplateServiceConfiguration
{
TemplateManager = templateManager,
BaseTemplateType = typeof(HtmlSupportTemplateBase<>)
};
Ответ 7
Мой ответ использует ответ Ханнеса Нукерманса.
Мне нужно было использовать RazorEngine для отправки электронных писем, содержащих строки HTML, хранящиеся в базе данных, чтобы их могли редактировать пользователи-администраторы.
Стандартная конфигурация не позволяла @Html.Raw работать.
В моем классе электронной почты я установил новый Engine.Razor(Engine является статическим), который включает в себя классы, которые рекомендует Ханнес. Мне был нужен только метод Raw, но вы можете добавить другие:
public class HtmlSupportTemplateBase<T> : TemplateBase<T>
{
public HtmlSupportTemplateBase()
{
Html = new MyHtmlHelper();
}
public MyHtmlHelper Html { get; set; }
}
public class MyHtmlHelper
{
/// <summary>
/// Instructs razor to render a string without applying html encoding.
/// </summary>
/// <param name="htmlString"></param>
/// <returns></returns>
public IEncodedString Raw(string htmlString)
{
return new RawString(WebUtility.HtmlEncode(htmlString));
}
}
Затем я мог бы использовать @Html.Raw в своем шаблоне электронных писем, чтобы включить редактируемый HTML
public class Emails
{
public static TemplateServiceConfiguration config
= new TemplateServiceConfiguration(); // create a new config
public Emails()
{
config.BaseTemplateType = typeof(HtmlSupportTemplateBase<>);// incorporate the Html helper class
Engine.Razor = RazorEngineService.Create(config);// use that config to assign a new razor service
}
public static void SendHtmlEmail(string template, EmailModel model)
{
string emailBody
= Engine.Razor.RunCompile(template, model.Type.ToString(), typeof(EmailModel), model);
следующее на самом деле не является частью ответа, но дает полезный код тем, кто использует его для электронных писем :)
var smtpClient = getStaticSmtpObject(); // an external method not included here
MailMessage message = new MailMessage();
message.From = new MailAddress(model.FromAddress);
message.To.Add(model.EmailAddress);
message.Subject = model.Subject;
message.IsBodyHtml = true;
message.Body = System.Net.WebUtility.HtmlDecode(emailBody);
smtpClient.SendAsync(message, model);
}
}
Затем я мог бы использовать его, передав строку, прочитанную из фактического шаблона .cshtml и модель, содержащую данные электронной почты. (ResolveConfigurationPath - это еще одна внешняя функция, которую я нашел на этой странице)
string template = System.IO.File.ReadAllText(ResolveConfigurationPath("~/Views/Emails/MAPEmail.cshtml"));
SendHtmlEmail(template, model);
Ответ 8
Модификация ответа mao47 для последнего синтаксиса бритвы, это также будет поддерживать частичные представления.
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Web.Hosting;
using System.Xml.Linq;
using RazorEngine.Configuration;
using RazorEngine.Templating;
public static class DynamicRazorTemplateParser
{
private static readonly IRazorEngineService service = RazorEngineService.Create(TemplateServiceConfiguration);
public static string RunCompile<T>(string template, string placeholder, T model, DynamicViewBag viewBag) where T : class
{
var templateSource = new LoadedTemplateSource(template);
return RunCompile(templateSource, placeholder, model, viewBag);
}
public static string RunCompile<T>(ITemplateSource template, string placeholder, T model, DynamicViewBag viewBag) where T : class
{
return service.RunCompile(template, placeholder, model.GetType(), model, viewBag);
}
public static string RunCompile(ITemplateSource template, string placeholder)
{
return service.RunCompile(template, placeholder);
}
private static TemplateServiceConfiguration TemplateServiceConfiguration
{
get
{
var config = new TemplateServiceConfiguration
{
BaseTemplateType = typeof(HtmlTemplateBase<>),
TemplateManager = new TemplateManager()
};
//TODO: Is this the best way?
var xDocument = XDocument.Load(AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "/Views/Web.config");
if (xDocument.Root != null)
{
var sysWeb = xDocument.Root.Element("system.web.webPages.razor");
if (sysWeb == null) return config;
var pages = sysWeb.Element("pages");
if (pages != null)
{
var namespaces = pages.Element("namespaces");
if (namespaces != null)
{
var namespacesAdd = namespaces.Elements("add")
.Where(x => x.Attribute("namespace") != null)
.Select(x =>
x.Attribute("namespace").Value
);
foreach (var ns in namespacesAdd)
{
config.Namespaces.Add(ns);
}
}
}
}
return config;
}
}
private class TemplateManager : ITemplateManager
{
private readonly ConcurrentDictionary<ITemplateKey, ITemplateSource> _dynamicTemplates = new ConcurrentDictionary<ITemplateKey, ITemplateSource>();
private readonly string baseTemplatePath;
public TemplateManager()
{
baseTemplatePath = HostingEnvironment.MapPath("~/Views/");
}
public ITemplateSource Resolve(ITemplateKey key)
{
ITemplateSource templateSource;
if (this._dynamicTemplates.TryGetValue(key, out templateSource))
return templateSource;
string template = key.Name;
var ubuilder = new UriBuilder();
ubuilder.Path = template;
var newURL = ubuilder.Uri.LocalPath.TrimStart('/');
string path = Path.Combine(baseTemplatePath, string.Format("{0}", newURL));
string content = File.ReadAllText(path);
return new LoadedTemplateSource(content, path);
}
public ITemplateKey GetKey(string name, ResolveType resolveType, ITemplateKey context)
{
return new NameOnlyTemplateKey(name, resolveType, context);
}
public void AddDynamic(ITemplateKey key, ITemplateSource source)
{
this._dynamicTemplates.AddOrUpdate(key, source, (k, oldSource) =>
{
if (oldSource.Template != source.Template)
throw new InvalidOperationException("The same key was already used for another template!");
return source;
});
}
}
}
using System;
using System.IO;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using RazorEngine.Templating;
using RazorEngine.Text;
// ReSharper disable ClassWithVirtualMembersNeverInherited.Global
// ReSharper disable MemberCanBePrivate.Global
namespace Common.Core.Razor
{
[RequireNamespaces("System.Web.Mvc.Html")]
public class HtmlTemplateBase<T> : RazorEngine.Templating.HtmlTemplateBase<T>, IViewDataContainer
{
private HtmlHelper<T> helper;
private ViewDataDictionary viewdata;
private TempDataDictionary tempdata;
private AjaxHelper<T> ajaxHelper;
private ViewContext viewContext;
private UrlHelper urlHelper;
private readonly RequestContext _requestContext = HttpContext.Current.Request.RequestContext;
public UrlHelper Url => urlHelper ?? (urlHelper = new UrlHelper(_requestContext));
public ViewContext ViewContext
{
get
{
if (viewContext != null) return viewContext;
viewContext = GetViewContext();
return viewContext;
}
}
public AjaxHelper<T> Ajax
{
get
{
if (ajaxHelper != null) return ajaxHelper;
ajaxHelper = new AjaxHelper<T>(ViewContext, this);
return ajaxHelper;
}
}
public HtmlHelper<T> Html
{
get
{
if (helper != null) return helper;
helper = new HtmlHelper<T>(ViewContext, this);
return helper;
}
}
public ViewDataDictionary ViewData
{
get
{
if (viewdata == null)
{
viewdata = new ViewDataDictionary
{
TemplateInfo = new TemplateInfo() { HtmlFieldPrefix = string.Empty }
};
if (Model != null)
{
viewdata.Model = Model;
}
}
return viewdata;
}
set
{
viewdata = value;
}
}
public TempDataDictionary TempData
{
get { return tempdata ?? (tempdata = new TempDataDictionary()); }
set
{
tempdata = value;
}
}
public virtual string RenderView()
{
using (var writer = new StringWriter())
{
ViewContext.View.Render(ViewContext, CurrentWriter);
return writer.GetStringBuilder().ToString();
}
}
private ViewContext GetViewContext()
{
if (HttpContext.Current == null) throw new NotImplementedException();
var requestContext = _requestContext;
var controllerContext = ControllerContext(requestContext);
var view = GetView(requestContext, controllerContext);
//Can't check if string writer is closed, need to catch exception
try
{
var vContext = new ViewContext(controllerContext, view, ViewData, TempData, CurrentWriter);
return vContext;
}
catch
{
using (var sw = new StringWriter())
{
var vContext = new ViewContext(controllerContext, view, ViewData, TempData, sw);
return vContext;
}
}
}
private IView GetView(RequestContext requestContext, ControllerContext controllerContext)
{
if ((string)requestContext.RouteData.DataTokens["Action"] != null)
{
requestContext.RouteData.Values["action"] = (string)requestContext.RouteData.DataTokens["Action"];
}
var action = requestContext.RouteData.GetRequiredString("action");
var viewEngineResult = ViewEngines.Engines.FindPartialView(controllerContext, action);
if (viewEngineResult != null && viewEngineResult.View != null)
{
return viewEngineResult.View;
}
viewEngineResult = ViewEngines.Engines.FindView(controllerContext, action, null);
if (viewEngineResult == null)
{
throw new Exception("No PartialView assigned in route");
}
return viewEngineResult.View;
}
public void SetView(string view)
{
_requestContext.RouteData.DataTokens["Action"] = view;
}
private ControllerContext ControllerContext(RequestContext requestContext)
{
ControllerBase controllerBase;
var routeDataValue = "EmptyController";
if (requestContext.RouteData.Values["controller"] != null && (string)requestContext.RouteData.Values["controller"] != routeDataValue)
{
var controllerName = (string)requestContext.RouteData.Values["controller"];
IController controller = ControllerBuilder.Current.GetControllerFactory().CreateController(requestContext, controllerName);
controllerBase = controller as ControllerBase;
}
else
{
var controller = new EmptyController();
controllerBase = controller; //ControllerBase implements IController which this returns
requestContext.RouteData.Values["controller"] = routeDataValue;
}
var controllerContext =
new ControllerContext(requestContext.HttpContext, requestContext.RouteData, controllerBase);
return controllerContext;
}
private class EmptyController : Controller { }
public override void WriteTo(TextWriter writer, object value)
{
if (writer == null)
throw new ArgumentNullException("writer");
if (value == null) return;
//try to cast to RazorEngine IEncodedString
var encodedString = value as IEncodedString;
if (encodedString != null)
{
writer.Write(encodedString);
}
else
{
//try to cast to IHtmlString (Could be returned by Mvc Html helper methods)
var htmlString = value as IHtmlString;
if (htmlString != null) writer.Write(htmlString.ToHtmlString());
else
{
//default implementation is to convert to RazorEngine encoded string
base.WriteTo(writer, value);
}
}
}
}
}