捕获BeginForm样式的一次性html帮助程序中的包装内容

时间:2012-04-04 14:22:22

标签: asp.net-mvc-3 html-helper

我正在尝试编写一个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限制。

2 个答案:

答案 0 :(得分:9)

实际上你可以有条件地隐藏具有类似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进行测试

答案 1 :(得分:6)

您无法有条件地呈现返回IDisposable的辅助方法的正文内容。它将始终呈现。当您想要使用using帮助程序对BeginForm元素进行自定义标记时,可以使用此样式的帮助程序。

您可以使用templated Razor delegate代替:

<form>

然后:

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);
            }
        });
    }
}