在MVC上强制使用自定义HtmlHelper扩展

时间:2012-06-14 08:33:01

标签: asp.net-mvc html-helper

Asp.net MVC提供了大量(非常有用的)HtmlHelper扩展。但是,如果我要提供一个扩展现有方法的扩展方法的微子框架呢?

即。 BeginForm可能会被重写为更丰富(总是添加防伪令牌和类似的安全内容。

问题

为了不重写Asp.net MVC的HTML帮助方法所有,如何强制使用我的?因此,使用通常的BeginForm会抛出异常或者首先不可访问。如果不从视图的文件夹 web.config 文件中删除System.Web.Mvc.Html命名空间,则可能无法进行第二种选择。这意味着那些帮助者的所有都需要重写。这是我不想做的事情。

问题在于,当使用这个微子框架时,出于安全原因应该防止使用标准帮助程序。周期。

还有哪些其他选择?

实施例

假设我只会编写自己的BeginForm我会称之为BeginSecureForm,因此可以将其用作:

@using Html.BeginSecureForm() {
    ...
    @Html.EditorFor(m => m.Something)
    ...
}

正如您所看到的,我也使用了自定义帮助程序和标准EditorFor帮助程序。这意味着仍然包含System.Web.Mvc.Html以使用EditorFor等非自定义帮助程序。

只要您使用我的自定义帮助程序方法,上层代码就可以正常工作......但是如果某些开发人员忘记这样做并使用普通代码呢?

@using Html.BeginForm() {
    ...
    @Html.EditorFor(m => m.Something)
    ...
}

在这种情况下,我想要:

  • Html.BeginForm根本无法访问
  • Html.BeginForm抛出了应该使用安全版本的异常
  • 我不知道的任何其他事情可以阻止使用标准BeginForm

2 个答案:

答案 0 :(得分:1)

实现这一目标的一种可能性是编写自定义WebViewPage并使用自定义属性覆盖Html属性:

public abstract class MyWebViewPage<T> : WebViewPage<T>
{
    public override void InitHelpers()
    {
        this.Ajax = new AjaxHelper<T>(ViewContext, this);
        this.Html = new MyHtmlHelper<T>(ViewContext, this);
        this.Url = new UrlHelper(ViewContext.RequestContext);
    }

    public new MyHtmlHelper<T> Html { get; set; }
}

这里是自定义MyHtmlHelper<T>类,您将在其中废弃您不希望开发人员直接使用的方法:

public class MyHtmlHelper<T>: HtmlHelper<T>
{
    public MyHtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer)
        : base(viewContext, viewDataContainer)
    {
    }

    [Obsolete("Use SecureBeginForm instead", true)]
    public MvcForm BeginForm()
    {
        throw new Exception("Use SecureBeginForm instead.");
    }
}

好的,现在剩下要做的就是切换应用程序中所有Razor视图的基本类型。这可以在~/Views/web.config内完成,您将替换:

<pages pageBaseType="System.Web.Mvc.WebViewPage">

使用:

<pages pageBaseType="MyAppName.Mvc.MyWebViewPage">

好的,现在您可以将您的微框架扩展方法编写到MyHtmlHelper类,从而提供默认方法的自定义安全对应方:

public static class MyHtmlHelperExtensions
{
    public static MvcForm SecureBeginForm<T>(this MyHtmlHelper<T> html)
    {
        var rawUrl = html.ViewContext.HttpContext.Request.Url.AbsoluteUri;
        var builder = new UriBuilder(rawUrl);
        builder.Scheme = Uri.UriSchemeHttps;
        var form = new TagBuilder("form");
        form.Attributes["action"] = builder.ToString();
        form.Attributes["method"] = "post";

        html.ViewContext.Writer.Write(form.ToString());
        return new MvcForm(html.ViewContext);
    }
}

现在在任何Razor视图中:

@using (Html.SecureBeginForm())
{
    ...
}

当你尝试时:

@using (Html.BeginForm())
{
    ...
}

你得到一个编译时错误(假设你已经启用了Razor视图的编译):

enter image description here

或运行时异常(如果还没有。)

答案 1 :(得分:0)

您正试图通过强制开发人员来避免使用Html.BeginForm而不是使用受保护的安全性来实现应用程序的安全性。假设你以某种方式欺骗框架不要使用Html.BeginForm那么什么?一个甚至不知道Html.BeginForm的年轻开发人员可以直接在视图中编写表单HTML并破坏规则!

我认为安全性必须在应用程序中实现,不要强迫某人使用正确的工具,而是必须在应用程序的更高级别完成。在表单示例中,如果发布到服务器的所有HTML表单都应该具有防伪标记,那么我将在MVC管道中的更高级别进行检查。如果某些开发人员使用普通表单,那么该模块将无法工作,并且在测试阶段将会小心。