【发布时间】:2009-07-11 17:06:58
【问题描述】:
好的,我承认 - 我为 ASP.NET MVC 编写了自己的视图状态工具。我对其他人的批评很感兴趣,尤其是考虑到与 WebForms 相关的所有视图状态抨击。另一方面,在 Pro ASP.NET MVC Framework (p405-406) 中,Steven Sanderson 说“我觉得作为一个通用的 Web 设计模式,[ViewState] 是完全合理的:Web 开发人员一直保留隐藏表单字段中的数据;这只是通过将该技术形式化并提供一个简洁的抽象层将其提升到一个新的水平。”考虑到我的具体问题,创建这样一个轻量级抽象层同时保留 MVC 的透明性和可测试性优势似乎是一种合理的方法。
问题形式:
- 使用 ViewData 是解决我的问题的最佳方法还是至少是一种强有力的方法?
- 在我的具体方法中是否存在严重的弱点(例如,性能、安全性)?
- 该方法与 MVC 设计美学的契合度如何?
- 有更好的解决方案吗?如果是这样,它是什么,为什么?
我正在编写一个安全界面来管理用户/角色/帐户——诸如此类。从数据库中检索的数据具有身份令牌和用于乐观并发控制的时间戳。对于编辑等操作,身份和时间戳必须与客户端操作相关联,这需要某种客户端持久性。时间戳是这种客户端持久性的关键驱动因素,因为更新记录需要根据当前时间戳检查检索时间戳,以查看自最初检索以来是否有其他用户对其进行了更新。必须保持时间戳的完整性,因为恶意用户可以通过操纵它来覆盖数据库记录。
通常的持久性选项是 ViewData、TempData 和会话状态。我没有认真考虑其他选项,例如编写自己的数据库工具。我选择 ViewData 是因为数据可以保留不止一次往返(例如,即使客户端跳转到另一个页面并返回,状态也会保留)并且因为我想避免大量的会话数据管理。我的想法是,如果仅将选择的数据存储在 ViewData 中并且使用 HMAC(散列码消息身份验证)代码进行保护,该方法的开销将相当低且安全。
在实践中,我使用一对函数 Encode/Decode 来序列化数据并计算 HMAC 代码,并使用 Html 助手 Html.FormState() 将序列化后的数据存储在表单上。 (编码/解码 API 比我展示的要复杂一些,使我能够存储多个对象等。)我还将状态作为参数传递回操作方法。这保持了具有功能性的设计,从而提高了可测试性。这是一个示例(对 ViewData 的内联分配仅用于说明):
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Edit(Guid? id) {
User user = _crmContext.Users.GetUser(id ?? Guid.Empty);
if (user == null) {
TempMessage = "User not found";
return RedirectToAction("Index");
}
else {
ViewData["formState"] = EncodeState("user", user);
return View(user);
}
}
[AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken]
public ActionResult Edit(Guid? id, string formState) {
User user = DecodeState("user", formState) as User;
if (user == null || id != user.UserId) {
TempMessage = "User not found";
return RedirectToAction("Index");
}
else {
try {
UpdateModel(user, "user");
_crmContext.Users.UpdateUser(user);
TempMessage = "User changes saved.";
return RedirectToAction("Details", new { id = user.UserId });
}
catch (RulesException e) {
e.AddModelStateErrors(ModelState, "user");
ViewData["formState"] = EncodeState("user", user);
return View(user);
}
}
}
public static string FormState(this HtmlHelper html) {
string anti = html.AntiForgeryToken();
string data = html.Hidden("formState");
return "\n" + anti + "\n" + data;
}
【问题讨论】:
-
在我看来是不合时宜的。
-
如果您决定使用 ViewState,为什么不尝试使用已经存在并使用 ASP.NET 开发的编码。虽然我敢打赌它不会那么直截了当 ;-)
-
@KingNestor:我很想知道您提出意见的具体原因。 @mfx:和你一样,我想 WebForms 视图状态机制与 Webforms 紧密相连。另外,EncodeState/DecodeState 非常轻量级,实际上只是一小部分代码。我花了大约 1/2 小时来改编 Pro ASP.NET MVC Framework 书中的一些代码。
-
其实并没有那么复杂,你只需创建一个新的 ObjectStateFormatter 实例,然后在其上调用 Serialize 或 Deserialize。然后,如果您在 web.config 中设置,它将使用机器密钥进行签名和加密。
-
感谢您的评论。我正在使用 LosFormatter 进行(反)序列化,而后者又在内部使用 ObjectStateFormatter。我将检查 ObjectStateFormatter 看看它是否提供了一些优势。也就是说,我实际上是在询问以我所描述的方式使用表单持久数据的价值,而不是关于如何实现持久性。
标签: asp.net-mvc