4

防止 MVC 4 过度发布的最佳方法是什么?

根据 MS 消息来源,[Bind] 属性应该是通过防止传入的表单值进入数据库来防止过度发布的最简单方法。使用最新版本的 MVC 和 EF,这似乎没有按预期/宣传的那样工作,除非我遗漏了一些重要的东西。

Wrox Professional ASP.NET MVC 4(Jon Galloway 的第 7 章)开始,以下类应防止过度发布:

[Bind(Exclude="IsAdmin")]
public class User
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public bool IsAdmin { get; set; }
}

但是 [Bind] 属性所做的只是阻止表单提交值绑定到模型。然后模型有一个空白/默认值,它被写回数据库。在这种情况下,它将确保您每次使用此模型调用 .SaveChanges() 时 IsAdmin = false。任何“真实”值都会被覆盖。这是一个巨大的安全失败。

另一种语法 - 将 [Bind] 放在 Edit 控制器操作参数中 - 执行完全相同的操作:

public ActionResult Edit([Bind(Exclude = "IsAdmin")] User user)

当调用 .SaveChanges() 时,所有“真实”值都会被覆盖,这与 K. Scott Allen 关于该主题的博客文章相矛盾:http: //odetocode.com/blogs/scott/archive/2012/03/11/complete-guide- to-mass-assignment-in-asp-net-mvc.aspx

唯一的选择似乎是一系列专用的 ViewModel 都与 Automapper 连接。虽然安全,但这似乎是一个非常令人头疼的问题,尤其是在:

  • 您可能对 Create、Edit、Index 和 Detail 操作有不同的要求,需要不同的 ViewModel
  • 您可能需要公开一些只读字段(例如 Edit 操作上的 CreatedBy),这些字段不能在属性上具有 [ReadOnly] 属性,因为它们是由 Create 操作更新的

我知道有人会回应说你永远不应该将数据模型绑定到视图,但这是默认的模板行为以及它在几乎所有文档中的显示方式。此外,MVC + EF 应该让生活更轻松,而不是更难,而与 AutoMapper 连接的大量 ModelView 类并不是我认为更容易的事情。

那么有人知道如何使 [Bind] 功能如宣传的那样吗?

4

2 回答 2

7

我想你可能在这个场合被 Wrox 的书误导了。您描述的是 Bind/Exclude 属性的预期行为。请参阅http://msdn.microsoft.com/en-us/library/system.web.mvc.bindattribute.exclude(v=vs.108).aspx

如果您不想将值绑定到模型上的每个属性,我相信 ViewModel 是他们要走的路,即使您正确指出它们是一种开销。尽管如此,使用它们的优势是显着的,在这种情况下,IMO 证明了额外的开发工作是合理的。例如:

  • 允许部分实体更新
  • 呈现来自多个实体的数据
  • 将 UI 与域模型解耦,允许您改变标签、验证规则、错误消息

Automapper 是从实体映射到视图模型的一种选择,但如果您使用的是延迟加载,请注意。我发现 Automapper 没有按照我希望的方式处理对 EF 代理类的更新。最后,我删除了 AM,并推出了我自己的基于 IMappable 接口和通用实用程序类的映射机制。在许多情况下,只需键入代码并配置 Automapper 即可。

于 2012-11-24T06:52:49.687 回答
0

如果您恢复为手动模型绑定,则应该没有任何问题。如果您不输入“IsAdmin”,您的模型将保留其原始值。这增加了几行额外的代码,但通过不生成不维护的 ViewModel 节省了大量时间。

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Guid id, FormCollection collection)
{
    var user = db.Users.Find(id);
    if (user != null)
        TryUpdateModel(user);
    else
        return HttpNotFound();
    if (ModelState.IsValid)
    {
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(user);
}
于 2013-07-31T21:01:32.443 回答