将选定的视图路径作为 HTML 注释注入到操作输出的开始和结束

发布于 2024-10-08 19:48:48 字数 1059 浏览 0 评论 0原文

我目前正在使用 MVC 3 组合一个多租户 Web 应用程序。至少 30 个不同的网站将共享一个公共代码库,并且在共享类似的底层功能的同时,它们需要看起来显着不同。因此,我使用 URL 在内部分离出客户端,并使用重写的 Razor 视图引擎(至少在查找视图方面)来自动选择给定操作的自定义视图或默认视图。

为了帮助“划分”一个复杂的页面,任何特定的视图都会使用多个部分视图,通常呈现为独立的操作,因此通常自定义视图或部分视图只会有很小的 HTML 差异,有助于最大限度地减少特定于客户的网站。

我希望你遵循这个背景!

因此,一个 HTML 页面可能由许多 HTML 部分视图的小片段组成,这些视图可能来自客户端的特定文件夹或通用版本。我希望通过轻松查看文件夹结构中他想要更改的 HTML 部分的位置,让我们的设计人员能够更轻松地对页面进行细微更改。

我的建议是,每个部分都将用 HTML 注释“括起来”,例如:

{ Partial 的内容 }

显然我可以手动将它们放入,但这只是自找麻烦,因为拼写错误,复制然后修改的客户端版本不被使用使用正确的 URL 进行更新。我认为应该可以从某些上下文中获取并注入它。

同时,我需要能够对某些操作不执行此操作。例如,部分可能会在文本区域内生成文本,因此注释在那里不合适。总的来说,我很高兴将这些评论放入除非我指出它不合适。

对我来说,这建议在某个操作上使用一个 ActionFilter,我可以在整个站点范围内应用该过滤器,然后针对某些操作关闭该过滤器。我希望其中一个可重写的事件能让我确定这条路径,但我似乎找不到它存储的任何地方。此外,OnResultExecuting似乎在选择Partial之前触发,并且OnResultExecuted似乎已经写出了Partial的内容,所以我无法在这里插入起始注释。我也找不到对所选部分路径的任何引用。

为了完整起见,我的目的是该属性仅在调试模式下编译时写入这些注释。

那么,有谁知道我如何能够获取所选视图的路径,而无需在 FindPartialView 和属性之间进行任何形式的修改?我的属性方法是最佳选择还是有更简单的方法来做到这一点?也许已经内置了一些东西!

非常感谢您的帮助。

I'm currently putting together a multi-tenancy web app using MVC 3. At least 30 different Web sites will share a common codebase, and while also sharing similar under-the-hood functionality, they are need to look significantly different. As a consequence I'm using the URL to internally separate out clients, and an overridden Razor view engine (at least in terms of finding the view) to automatically select either a customised view for a given action, or the default view.

To help 'compartmentalise' a complex page, any particular view will make use of several partials, usually rendered as self-contained actions, so that generally a custom view or partial view will only have small HTML differences, helping to minimise any code specific to a client's site.

I hope you followed that background!

So one HTML page might be made up of lots of little smatterings of HTML partial views, which could come from specific folders for the client, or a general-purpose version. I'm hoping to make it easier for our designer to make minor changes to a page by easily seeing where in the folder structure the bit of HTML he wants to change are.

My proposal then is that each partial will be 'bracketed' with HTML comments such as:

{ Content of partial }

Obviously I could put these in manually, but that's just asking for trouble, for typos, for copied and then modified client versions not being updated with the correct URL. It should be possible to get this from some context and inject it, I think.

At the same time, I need to be able to not do this for certain Actions. Eg, a partial might be generating text inside a textarea, say, so the comments wouldn't be appropriate there. On the whole I'm happy to put these comments in unless I specify that it's not appropriate.

For me this suggests an ActionFilter on an Action, which I can apply site wide and then turn off for certain Actions. I'd hope that one of the overridable events would let me ascertain this path, but I can't seem to find anywhere it's stored. Furthermore, OnResultExecuting seems to fire before the Partial has been selected, and OnResultExecuted seems to have already written out the contents of the Partial, so I can't insert the starting comment here. I also can't find any reference to the path of the selected partial.

Just for completeness, it's my intention that this attribute would only write these comments when compiled in Debug mode.

So, does anyone know how I might be able to get the path to the selected View without any kind of hack between FindPartialView and the Attribute? Is my Attribute method the best choice or is there an easier way to do this? Perhaps something's built in already!

Many thanks for your help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

纸短情长 2024-10-15 19:48:48

好吧,我从来没有忘记想要这个,并且一直希望有一天我能解决它,谢天谢地我做到了。

我已经覆盖了默认的 WebViewPage(我使用 Razor 引擎),特别是 ExecutePageHierarchy 来注入注释:

public abstract class PaladinWebViewPage : PaladinWebViewPage<dynamic>
{
}

public abstract class PaladinWebViewPage<TModel> : WebViewPage<TModel>
{
    public bool DisplaySourceCodeComments
    {
        get { return ((bool?) ViewBag.__DisplaySourceCodeComments) ?? false; }
        set { ViewBag.__DisplaySourceCodeComments = value; }
    }

    public override void ExecutePageHierarchy()
    {
        base.ExecutePageHierarchy();

        // Filters can be used to set and clear this value so we can decide when to show this comment
        if (!DisplaySourceCodeComments) return;

        var sw = Output as StringWriter;
        if (sw == null) return;

        var sb = sw.GetStringBuilder();
        sb.Insert(0, string.Format("<!-- Start of {0} -->", VirtualPath));
        sb.AppendFormat("<!-- End of {0} -->", VirtualPath);
    }

VirtualPath 告诉我们用于构建 HTML 的确切文件,因此我们可以在之前和之后注入文件名。目前这没有执行任何操作,因为默认情况下不显示注释(DisplaySourceCodeComments 中的“?? false”)。

另外,要使用此视图页面,您需要编辑 Views/Web.config 并将 pageBaseType 更改为此类型。

我想有选择地打开和关闭这些评论,因此我创建了一个 ActionFilter:

public class DisplaySourceCodeCommentsAttribute : ActionFilterAttribute
{
    private readonly bool _displaceSourceCodeComments;

    public DisplaySourceCodeCommentsAttribute(bool displaceSourceCodeComments)
    {
        _displaceSourceCodeComments = displaceSourceCodeComments;
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);

        var viewResult = filterContext.Result as ViewResultBase;
        if (viewResult == null) return;

        viewResult.ViewBag.__DisplaySourceCodeComments = _displaceSourceCodeComments;
    }
}

我有点不高兴,因为我必须在此处使用 ViewBag,并且在视图页面覆盖中单独使用,因为它们没有紧密链接,但我找不到过滤器直接与视图页面交互的方法,所以这是一个必要的捏造。它确实有一个好处,即显示视图或部分的源代码也会自动显示任何子部分的源代码,直到您再次将其关闭,因为 ViewBag 是沿着链传递的。

有了这个,任何操作都可以打开源代码注释
[DisplaySourceCodeComments(true)]

或者,显然用 false 再次将其关闭

该属性检查上下文结果是否为 ViewResultBase,这意味着只是视图和部分,因此 Json 或内容或重定向不受影响,这也非常方便。

最后,我在调试模式下运行时将此操作过滤器设置为全局,以便每个视图和部分视图都包含源注释,方法是将以下行添加到 global.asax.cs:

[#]if DEBUG

        // When in debug mode include HTML comments showing where a view or partial has come from
        GlobalFilters.Filters.Add(new DisplaySourceCodeCommentsAttribute(true));

[#]endif

I'我真的很高兴我终于把它解决了,所以我希望这对其他人有用。

Well, I've never forgotten about wanting this, and always hoped I'd solve it one day, and thankfully I have.

I've overridden the default WebViewPage (I use the Razor engine), and in particular ExecutePageHierarchy to inject the comments:

public abstract class PaladinWebViewPage : PaladinWebViewPage<dynamic>
{
}

public abstract class PaladinWebViewPage<TModel> : WebViewPage<TModel>
{
    public bool DisplaySourceCodeComments
    {
        get { return ((bool?) ViewBag.__DisplaySourceCodeComments) ?? false; }
        set { ViewBag.__DisplaySourceCodeComments = value; }
    }

    public override void ExecutePageHierarchy()
    {
        base.ExecutePageHierarchy();

        // Filters can be used to set and clear this value so we can decide when to show this comment
        if (!DisplaySourceCodeComments) return;

        var sw = Output as StringWriter;
        if (sw == null) return;

        var sb = sw.GetStringBuilder();
        sb.Insert(0, string.Format("<!-- Start of {0} -->", VirtualPath));
        sb.AppendFormat("<!-- End of {0} -->", VirtualPath);
    }

VirtualPath tells us the exact file used to build the HTML, so we can inject the filename before and after. This isn't doing anything at the moment, since the default is to not show comments (the "?? false" in DisplaySourceCodeComments).

Also to use this view page you need to edit Views/Web.config and change the pageBaseType to this type.

I want to selectively turn these comments on and off so I've created an ActionFilter:

public class DisplaySourceCodeCommentsAttribute : ActionFilterAttribute
{
    private readonly bool _displaceSourceCodeComments;

    public DisplaySourceCodeCommentsAttribute(bool displaceSourceCodeComments)
    {
        _displaceSourceCodeComments = displaceSourceCodeComments;
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);

        var viewResult = filterContext.Result as ViewResultBase;
        if (viewResult == null) return;

        viewResult.ViewBag.__DisplaySourceCodeComments = _displaceSourceCodeComments;
    }
}

I'm slightly unhappy that I've had to use the ViewBag here and also separately in the view page override, as they aren't tightly linked, but I can't find a way for the filter to directly interact with the view page, so this is something of a necessary fudge. It does have the benefit that displaying source code for a view or partial also automatically displays it for any child partials until you turn it off again, since the ViewBag is passed down the chain.

With this in place, any action can turn on the source code comments with
[DisplaySourceCodeComments(true)]

or, obviously turn it off again with false

The Attribute checks that the context result is a ViewResultBase, which means just Views and Partials, so Json or Content or redirects aren't affected, which is very handy too.

Finally, I make this action filter a global when running in debug mode so that every view, and partial has the source comment included, by adding the following line to global.asax.cs:

[#]if DEBUG

        // When in debug mode include HTML comments showing where a view or partial has come from
        GlobalFilters.Filters.Add(new DisplaySourceCodeCommentsAttribute(true));

[#]endif

I'm really happy I've finally got it sorted so I hope this is useful for someone else.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文