gpt4 book ai didi

c# - 在 View 中渲染包含 Razor 代码的字符串

转载 作者:行者123 更新时间:2023-11-30 15:22:17 25 4
gpt4 key购买 nike

此处考虑 CMS 用例。想象一下这样的 View :

// /Home/Index.cshtml
@model object
@{
var str = "My <b>CMS</b> content with razor code: @Html.ActionLink(\"Click\", \"Home\")"
}
@Html.MyCustomRazorStringRenderer(Model)

预期输出:

My <b>CMS</b> content with razor code: <a href="/Home/Click">Click</a>

MyCustomRazorStringRenderer 是什么样子的?它必须以某种方式做某事。喜欢创建/使用 ViewContext 并呈现它(如此处:Render a view as a string),但我无法完全理解它。

最佳答案

您必须创建一个包含 extension method 的静态类.该方法必须返回 MvcHtmlString 的一个实例包含安全呈现的 HTML 输出。话虽如此,正确地访问 renderedOutput 意味着“劫持”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 所擅长的——将 View 保存在一个好的位置,预编译它们,并在引用时调用它们。 更新:好吧,也许大量的缓存可能会有所帮助,但我实际上仍然不推荐这种解决方案。

生成代码时,Razor 发出对 this.Writethis.WriteLiteral 的调用。因为 this 是一个继承自您自己编写的基类的对象,所以由您提供 WriteWriteLiteral 的实现。

如果您在模板字符串中使用任何其他 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<> 类

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" })

更新

“实时”执行此操作 - 如果您不大量使用缓存,让 Razor 为每个页面加载编译和运行显然太慢,但如果您分解我的代码片段并让您的 CMS 自动请求重新编译每当页面内容发生变化时,您都可以在这里做一些非常有趣的事情。

关于c# - 在 View 中渲染包含 Razor 代码的字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35913730/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com