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

ASP.NET Core:如何检测标签助手是否在父元素中渲染?

问题

在ASP.NET Core中,能否检测标签助手是否在局部视图中渲染?

开发了一个脚本标签助手,功能是缓存要渲染的HTML,之后通过中间件在</body>标签前统一渲染。但如果该标签助手在局部视图中被调用,希望它直接在原地渲染,而非缓存后统一输出。

尝试过两种方案,但都存在问题:

  • 方案一:添加body标签助手,在请求缓存中存入标记供脚本标签助手检测,但布局页的body标签助手渲染时机晚于视图中的脚本标签助手,导致检测无效。
  • 方案二:在Action中存储标记变量,但每次返回局部视图都要手动设置,操作繁琐且容易遗漏。

以下是参考代码:

原ScriptTagHelper代码

public class ScriptTagHelper : TagHelper {
    private readonly IHtmlManager _htmlManager;

    public ScriptTagHelper(IHtmlManager htmlManager) {
        _htmlManager = htmlManager;
    }

    public string? At { get; set; }

    [HtmlAttributeNotBound, ViewContext]
    public ViewContext ViewContext { get; set; } = default!;

    public override void Process(TagHelperContext context, TagHelperOutput output) {
        if (!string.IsNullOrEmpty(At)) {
            output.SuppressOutput();

            var builder = new TagBuilder("script") {
                TagRenderMode = TagRenderMode.Normal
            };

            foreach (var attribute in output.Attributes) {
                builder.Attributes.Add(attribute.Name, attribute.Value.ToString());
            }

            _htmlManager.RegisterHtml(At, builder);
        }
    }
}

原中间件代码

public class RenderHtmlMiddleware {
    private readonly RequestDelegate _next;

    public RenderHtmlMiddleware(RequestDelegate next) {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext httpContext, IHtmlManager htmlManager) {
        var originBody = httpContext.Response.Body;

        using var body = new MemoryStream();
        httpContext.Response.Body = body;

        try {
            await _next(httpContext);
        } finally {
            httpContext.Response.Body = originBody;
        }

        if (body.Length > 0) {
            var content = Encoding.UTF8.GetString(body.ToArray());

            foreach (var position in Enum.GetValues<HtmlPosition>()) {
                var html = await htmlManager.GetHtmlAsync(position);
                var innerHtml = string.Join("", html.Select(h => h.ToHtmlString()));

                // 省略不同位置的处理代码
                
                content = content.Replace("</body>", string.Concat(innerHtml, Environment.NewLine, "</body>"));
            }

            httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(content);

            await httpContext.Response.WriteAsync(content);
        }
    }
}

使用方式

<script at="@HtmlPosition.BodyPostContent" src="foo"></script>

解决方案

可以通过ViewContext直接判断当前视图是否为局部视图,以下是两种可靠的实现方式:

1. 直接使用IsPartial属性(推荐,适用于ASP.NET Core 3.0+)

ASP.NET Core 3.0及以上版本中,ViewContext新增了IsPartial属性,内部已处理所有局部视图场景(包括PartialViewResult、命名以_Partial开头的视图、组件渲染等),无需手动判断路径。

修改ScriptTagHelper的Process方法:

public override void Process(TagHelperContext context, TagHelperOutput output) {
    // 如果是局部视图,直接保留原标签输出,不执行缓存逻辑
    if (ViewContext.IsPartial) {
        return;
    }

    if (!string.IsNullOrEmpty(At)) {
        output.SuppressOutput();

        var builder = new TagBuilder("script") {
            TagRenderMode = TagRenderMode.Normal
        };

        foreach (var attribute in output.Attributes) {
            builder.Attributes.Add(attribute.Name, attribute.Value.ToString());
        }

        _htmlManager.RegisterHtml(At, builder);
    }
}

2. 兼容低版本的判断方式(适用于ASP.NET Core 2.x)

如果项目基于ASP.NET Core 2.x,可通过对比执行路径和视图路径,或判断当前结果类型来识别局部视图:

public override void Process(TagHelperContext context, TagHelperOutput output) {
    // 两种判断逻辑结合,覆盖更多局部视图场景
    bool isPartial = ViewContext.ExecutingFilePath != ViewContext.View.Path 
                     || ViewContext.Result is PartialViewResult;

    if (isPartial) {
        return;
    }

    // 原缓存逻辑...
}

补充说明

  • 优先使用IsPartial属性,它是官方提供的标准判断方式,覆盖场景更全面,无需维护自定义判断逻辑。
  • 无需修改中间件代码,因为局部视图中的脚本标签助手会直接输出内容,不会被缓存到IHtmlManager中,中间件自然不会处理这部分内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 08:44:55