c#-在视图中呈现包含剃刀代码的字符串

在这里考虑CMS用例.想象这样一个视图:

// /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的静态类.该方法必须返回包含安全呈现的HTML输出的MvcHtmlString实例.话虽如此,正确地到达renderOutput意味着“劫持”了Razor渲染器,这很棘手.

您真正要做的是在预期的环境之外使用Razor引擎,其描述如下:http://vibrantcode.com/blog/2010/7/22/using-the-razor-parser-outside-of-aspnet.html

这里也有很多很好的信息,从中我得到了很多下面代码的启发:http://www.codemag.com/article/1103081

这些类的起点是:RazorEngineHostRazorTemplateEngineCSharpCodeProviderHtmlHelper.

工作代码

我实际上得到了几乎可行的版本,但是意识到这是一件非常徒劳的事情. Razor引擎通过生成代码来工作,然后必须使用CSharpCodeProvider对其进行编译.这需要时间.很多时间!

唯一可行且有效的方法是将模板字符串保存在某处,对其进行预编译,并在调用时调用这些已编译的模板.这使它基本上对您的使用毫无用处,因为这正是带有Razor的ASP.NET MVC所擅长的-将视图保存在适当的位置,对其进行预编译,并在引用时调用它们.更新:嗯,也许大量的缓存可能会有所帮助,但是我仍然不会实际推荐此解决方案.

生成代码时,Razor发出对this.Write和this.WriteLiteral的调用.因为这是从您自己编写的基类继承的对象,所以由您提供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<>类

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每当内容出现时都自动请求重新编译页面更改后,您可以在此处做一些非常有趣的事情.

上一篇:javascript – 将js变量或inout值传递给razor


下一篇:javascript-在IIS 8(Windows 8)上部署ASP.Net MVC应用程序时未呈现CSS和JS文件