9

我正在尝试编写一个 BeginForm 样式的 html 助手,它使用 IDisposable 来包装其他代码。我希望助手仅在满足特定条件时才呈现包装代码(例如,用户处于特定角色)。

我认为我可以简单地在 Begin 方法中切换 context.Writer 并在 Dispose 方法中将其切换回。下面的代码编译并运行,但在所有情况下都会呈现包装的内容。如果我单步执行,则包装的内容不会写入新的 StringWriter,因此不在我的控制范围内。

    public static IDisposable BeginSecure(this HtmlHelper html, ...)
    {
        return new SecureSection(html.ViewContext, ...);
    }

    private class SecureSection : IDisposable
    {
        private readonly ViewContext _context;
        private readonly TextWriter _writer;

        public SecureSection(ViewContext context, ...)
        {
            _context = context;
            _writer = context.Writer;
            context.Writer = new StringWriter();
        }

        public void Dispose()
        {
            if (condition here)
            {
                _writer.Write(_context.Writer);
            }

            _context.Writer = _writer;
        }
    }

我正在尝试使用 html 助手做的事情吗?

我知道 razor 中的声明性 html 助手可能会起作用,但如果可能的话,我会更喜欢标准的 html 助手方法,因为 MVC3 中 razor 助手的 app_code 限制。

4

2 回答 2

10

实际上,您可以使用类似 BeginForm 的结构有条件地隐藏内容。它只涉及稍微弄乱内部 StringBuilder :

public class Restricted: IDisposable
{
    public bool Allow { get; set; }

    private StringBuilder _stringBuilderBackup;
    private StringBuilder _stringBuilder;
    private readonly HtmlHelper _htmlHelper;

    /// <summary>
    /// Initializes a new instance of the <see cref="Restricted"/> class.
    /// </summary>
    public Restricted(HtmlHelper htmlHelper, bool allow)
    {
        Allow = allow;
        _htmlHelper = htmlHelper;
        if(!allow) BackupCurrentContent();
    }

    private void BackupCurrentContent()
    {
        // make backup of current buffered content
        _stringBuilder = ((StringWriter)_htmlHelper.ViewContext.Writer).GetStringBuilder();
        _stringBuilderBackup = new StringBuilder().Append(_stringBuilder);
    }

    private void DenyContent()
    {
        // restore buffered content backup (destroying any buffered content since Restricted object initialization)
        _stringBuilder.Length = 0;
        _stringBuilder.Append(_stringBuilderBackup);
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    public void Dispose()
    {
        if(!Allow)
            DenyContent();
    }
}

然后你只需要制作一个 HtmlHelper 来制作上述对象的实例

public static class RestrictedHelper
{
    public static Restricted RestrictedContent(this HtmlHelper htmlHelper, bool allow)
    {
        return new Restricted(htmlHelper, allow);
    }
}

用法如下:

@using (var restricted = Html.Restricted(true))
{
    <p>This will show up</p>
}
@using (var restricted = Html.Restricted(false))
{
    <p>This won't</p>
}

好处:

  • 编写自定义逻辑以显示/隐藏您的内容并将其传递给 Restricted 构造函数。
  • Restricted 对象中的公共属性可以在视图中的代码块中访问,因此您可以在那里重用计算值。

用 ASP.Net MVC 4 测试

于 2013-01-22T18:46:28.977 回答
6

您不能有条件地呈现辅助方法返回的主体内容IDisposable。它会一直渲染。当你想using用一些自定义标记包装块的主体时,你可以使用这种风格的助手,比如BeginForm助手对<form>元素所做的。

您可以使用 atemplated Razor delegate代替:

public static class HtmlExtensions
{
    public static HelperResult Secure(this HtmlHelper html, Func<object, HelperResult> template)
    {
        return new HelperResult(writer =>
        {
            if (condition here)
            {
                template(null).WriteTo(writer);
            }
        });
    }
}

进而:

@Html.Secure(
    @<div>
         You will see this text only if some condition is met
    </div>
)
于 2012-08-14T11:39:47.410 回答