在这里考虑CMS用例。想象一下这样的观点:
My <b>CMS</b> content with razor code: <a href="/Home/Click">Click</a>
预期产出:
{{1}}
MyCustomRazorStringRenderer是什么样的?它必须以某种方式做某事。比如创建/使用ViewContext并渲染它(就像这里:ecalllist.h)但我无法理解它。
答案 0 :(得分:4)
您必须创建一个包含extension method的静态类。该方法必须返回包含安全呈现的HTML输出的MvcHtmlString实例。话虽如此,正确地进入renderedOutput
意味着劫持&#34; Razor渲染器,这很棘手。
您真正在做的是在其预期环境之外使用Razor引擎,如下所述:http://vibrantcode.com/blog/2010/7/22/using-the-razor-parser-outside-of-aspnet.html
这里还有很多很好的信息,我从中获得了很多以下代码的灵感:http://www.codemag.com/article/1103081
这些课程的出发点是:RazorEngineHost,RazorTemplateEngine,CSharpCodeProvider,HtmlHelper。
工作代码
我实际上有一个几乎可以工作的版本,但意识到这是一件非常徒劳的事情。 Razor引擎通过生成代码来工作,然后必须使用CSharpCodeProvider
进行编译。这需要时间。很多时候!
唯一可行且有效的方法是将模板字符串保存在某处,预编译它们,并在调用时调用这些编译模板。这使得它对你所追求的东西基本没用,因为这正是带有Razor的ASP.NET MVC所擅长的 - 将Views放在一个好位置,预编译它们,并在引用时调用它们。 更新 :好吧,也许大量的缓存可能有所帮助,但我仍然不会推荐这个解决方案。
生成代码时,Razor会发出对this.Write
和this.WriteLiteral
的调用。由于this
是从您自己编写的基类继承的对象,因此您需要提供Write
和WriteLiteral
的实现。
如果您在模板字符串中使用任何其他HtmlHelper
扩展名,则需要包含所有这些扩展名的程序集引用和名称空间导入。下面的代码添加了最常见的代码。由于匿名类型的性质,它们不能用于模型类。
MyRazorExtensions类
public static class MyRazorExtensions
{
public static MvcHtmlString RazorEncode(this HtmlHelper helper, string template)
{
return RazorEncode(helper, template, (object)null);
}
public static MvcHtmlString RazorEncode<TModel>(this HtmlHelper helper, string template, TModel model)
{
string output = Render(helper, template, model);
return new MvcHtmlString(output);
}
private static string Render<TModel>(HtmlHelper helper, string template, TModel model)
{
// 1. Create a host for the razor engine
// TModel CANNOT be an anonymous class!
var host = new RazorEngineHost(RazorCodeLanguage.GetLanguageByExtension("cshtml");
host.DefaultNamespace = typeof(MyTemplateBase<TModel>).Namespace;
host.DefaultBaseClass = nameof(MyTemplateBase<TModel>) + "<" + typeof(TModel).FullName + ">";
host.NamespaceImports.Add("System.Web.Mvc.Html");
// 2. Create an instance of the razor engine
var engine = new RazorTemplateEngine(host);
// 3. Parse the template into a CodeCompileUnit
using (var reader = new StringReader(template))
{
razorResult = engine.GenerateCode(reader);
}
if (razorResult.ParserErrors.Count > 0)
{
throw new InvalidOperationException($"{razorResult.ParserErrors.Count} errors when parsing template string!");
}
// 4. Compile the produced code into an assembly
var codeProvider = new CSharpCodeProvider();
var compilerParameters = new CompilerParameters { GenerateInMemory = true };
compilerParameters.ReferencedAssemblies.Add(typeof(MyTemplateBase<TModel>).Assembly.Location);
compilerParameters.ReferencedAssemblies.Add(typeof(TModel).Assembly.Location);
compilerParameters.ReferencedAssemblies.Add(typeof(HtmlHelper).Assembly.Location);
var compilerResult = codeProvider.CompileAssemblyFromDom(compilerParameters, razorResult.GeneratedCode);
if (compilerResult.Errors.HasErrors)
{
throw new InvalidOperationException($"{compilerResult.Errors.Count} errors when compiling template string!");
}
// 5. Create an instance of the compiled class and run it
var templateType = compilerResult.CompiledAssembly.GetType($"{host.DefaultNamespace}.{host.DefaultClassName}");
var templateImplementation = Activator.CreateInstance(templateType) as MyTemplateBase<TModel>;
templateImplementation.Model = model;
templateImplementation.Html = helper;
templateImplementation.Execute();
// 6. Return the html output
return templateImplementation.Output.ToString();
}
}
MyTemplateBase&lt;&gt;类强>
public abstract class MyTemplateBase<TModel>
{
public TModel Model { get; set; }
public HtmlHelper Html { get; set; }
public void WriteLiteral(object output)
{
Output.Append(output.ToString());
}
public void Write(object output)
{
Output.Append(Html.Encode(output.ToString()));
}
public void Write(MvcHtmlString output)
{
Output.Append(output.ToString());
}
public abstract void Execute();
public StringBuilder Output { get; private set; } = new StringBuilder();
}
<强> test.cshtml 强>
@using WebApplication1.Models
<h2>Test</h2>
@Html.RazorEncode("<p>Paragraph output</p>")
@Html.RazorEncode("<p>Using a @Model</p>", "string model" )
@Html.RazorEncode("@for (int i = 0; i < 100; ++i) { <p>@i</p> }")
@Html.RazorEncode("@Html.ActionLink(Model.Text, Model.Action)", new TestModel { Text = "Foo", Action = "Bar" })
<强>更新强>
这样做&#34;直播&#34; - 如果你不重视缓存,那么让Razor编译并运行每个页面加载显然太慢了,但是如果你分解我的代码并让你的CMS在页面内容时自动重新编译改变,你可以在这里做一些非常有趣的事情。