You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core Razor动态编译视图中嵌套Razor语法解析问题

解决方案:让动态Razor片段在编译阶段解析执行

你的核心问题是:存储在模型属性中的Razor语法字符串未被编译器解析,而是直接作为文本输出。要实现嵌套Razor语法的编译执行,同时避免每次请求解析,可以通过预编译动态Razor模板并缓存的方式解决,以下是具体方案:

1. 实现Razor模板编译服务

创建一个专门的服务,负责将字符串形式的Razor模板编译为可执行委托,并缓存编译结果,仅在模板内容变更时重新编译。

using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Concurrent;

public interface IDynamicRazorCompiler
{
    Task<string> RenderAsync(string razorTemplate, ViewContext viewContext);
}

public class DynamicRazorCompiler : IDynamicRazorCompiler
{
    private readonly RazorTemplateEngine _razorEngine;
    private readonly IServiceProvider _serviceProvider;
    private readonly ConcurrentDictionary<string, Func<ViewContext, Task<string>>> _compiledTemplates = new();

    public DynamicRazorCompiler(RazorTemplateEngine razorEngine, IServiceProvider serviceProvider)
    {
        _razorEngine = razorEngine;
        _serviceProvider = serviceProvider;
    }

    public async Task<string> RenderAsync(string razorTemplate, ViewContext viewContext)
    {
        // 用模板内容生成唯一缓存键
        var cacheKey = Convert.ToBase64String(System.Security.Cryptography.MD5.HashData(Encoding.UTF8.GetBytes(razorTemplate)));
        
        if (!_compiledTemplates.TryGetValue(cacheKey, out var renderFunc))
        {
            // 解析Razor模板
            var document = _razorEngine.ParseTemplate(razorTemplate);
            var codeDocument = _razorEngine.GenerateCode(document);

            // 配置编译所需的程序集引用
            var references = new[]
            {
                MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(ViewContext).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(System.Security.Claims.ClaimsPrincipal).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(Microsoft.AspNetCore.Http.HttpContext).Assembly.Location),
                // 添加你的项目程序集引用
                MetadataReference.CreateFromFile(Assembly.GetExecutingAssembly().Location)
            };

            // 编译生成的C#代码
            var compilation = CSharpCompilation.Create(
                $"DynamicRazorTemplate_{cacheKey}",
                new[] { codeDocument.GeneratedCode },
                references,
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            // 输出编译结果到内存流
            using var ms = new MemoryStream();
            var emitResult = compilation.Emit(ms);
            
            if (!emitResult.Success)
            {
                var errors = string.Join(Environment.NewLine, emitResult.Diagnostics.Select(d => d.ToString()));
                throw new InvalidOperationException($"模板编译失败:{errors}");
            }

            // 加载编译后的程序集
            ms.Seek(0, SeekOrigin.Begin);
            var assembly = Assembly.Load(ms.ToArray());
            var pageType = assembly.GetType("AspNetCore.GeneratedPages.DynamicTemplate");
            
            // 创建渲染委托
            renderFunc = async (context) =>
            {
                var razorPage = (IRazorPage)Activator.CreateInstance(pageType);
                razorPage.PageContext = context.PageContext;
                razorPage.ViewContext = context;

                using var writer = new StringWriter();
                context.Writer = writer;
                await razorPage.ExecuteAsync();
                return writer.ToString();
            };

            _compiledTemplates.TryAdd(cacheKey, renderFunc);
        }

        return await renderFunc(viewContext);
    }
}

2. 注册服务

在Program.cs中注册这个服务:

builder.Services.AddScoped<IDynamicRazorCompiler, DynamicRazorCompiler>();
// 注册RazorTemplateEngine实例
builder.Services.AddSingleton<RazorTemplateEngine>(sp =>
{
    var razorEngine = sp.GetRequiredService<RazorEngine>();
    var options = sp.GetRequiredService<IOptions<RazorViewEngineOptions>>().Value;
    return new RazorTemplateEngine(razorEngine, options);
});

3. 在视图中使用

修改视图代码,注入服务并调用解析逻辑:

@inject IDynamicRazorCompiler _dynamicRazorCompiler

<p>@if (!string.IsNullOrEmpty(Model.MyPropertyOnTheModel))
{
    @Html.Raw(await _dynamicRazorCompiler.RenderAsync(Model.MyPropertyOnTheModel, ViewContext))
}</p>

4. 安全注意事项

  • 严格限制能编辑Razor模板内容的用户范围,杜绝代码注入风险。
  • 可对模板内容做白名单过滤,仅允许访问安全的内置对象(如@User.Identity.Name)。

替代方案:将模板作为独立视图存储

如果动态内容本身就是视图的一部分,可直接将带Razor语法的内容存储在数据库中作为完整视图片段,通过DbFileProvider加载时直接编译整个视图。这种方式更贴合你现有的编译机制,但仅适用于内容变更时允许重新编译视图的场景。

内容的提问来源于stack exchange,提问作者Mark Redfern

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 15:35:00