我正在使用 ASP.NET MVC 2.0,我正在尝试利用我的控制器中的模型绑定以及模型状态验证。但是,我遇到了一个问题,并想在这里与人们分享,看看您的想法。
好的,我的模型类库中有我干净的用户 poco ......
namespace Model
{
public partial class User
{
public virtual int Id { get; private set; }
public virtual string UserName { get; private set; }
public virtual string DisplayName { get; set; }
public virtual string Email { get; set; }
public User(string displayName, string userName)
: this()
{
DisplayName = displayName;
UserName = userName;
}
}
}
我所采用的设计只允许在构建对象后编辑某些属性。例如,UserName 只能在构造对象时设置,对我来说这有 OO 意义,但这是我的问题的关键,所以我想在这里强调它。
然后我有一个“伙伴类”,它为我的用户类定义了验证元数据......
namespace Model
{
[MetadataType(typeof(UserMetadata))]
public partial class User
{
class UserMetadata
{
[Required]
public virtual int Id { get; set; }
[Required]
public virtual string UserName { get; set; }
[Required]
public virtual string DisplayName { get; set; }
[RegularExpression(@"^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$", ErrorMessage = "Invalid address")]
public virtual string Email { get; set; }
}
}
}
然后在我的 web 层中,我想让我的用户能够编辑这个对象。所以我的配置文件控制器中有以下两种操作方法。
namespace Web.Controllers
{
public class ProfileController : Controller
{
[Authorize]
public ActionResult Edit()
{
var user = _session.Single<User>(x => x.UserName == HttpContext.User.Identity.Name );
return View(user);
}
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize]
[TransactionFilter]
public ActionResult Edit(User updatedUser)
{
// Get the current user to update.
var user = _session.Single<User>(x => x.UserName == HttpContext.User.Identity.Name);
if (ModelState.IsValid)
{
TryUpdateModel(user);
// Update store...
}
return View(updatedUser);
}
}
}
这有一个强类型的视图来配合它......
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Model.User>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Edit
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<%=Html.Script("jquery.validate.js")%>
<%=Html.Script("MicrosoftMvcJQueryValidation.js")%>
<%=Html.Script("MvcFoolproofJQueryValidation.js")%>
<div class="container">
<div class="column span-14">
<% using (Html.BeginForm()) {%>
<%= Html.AntiForgeryToken() %>
<fieldset>
<%: Html.DisplayFor(model => model.UserName) %>
<%= Html.Label("Display Name") %>
<%= Html.HiddenFor(model => model.DisplayName)%>
<%= Html.ValidationMessageFor(model => model.DisplayName)%>
<%= Html.Label("Email address") %>
<%= Html.EditorFor(model => model.Email)%>
<%= Html.ValidationMessageFor(model => model.Email)%>
<%= Html.HiddenFor(model => model.UserName)%>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
</div>
<div class="clear"></div>
<% } %>
</div>
</asp:Content>
好的,这就是所有的代码!
所以这就是问题所在,在初始获取请求后视图渲染得很好。但是当用户发回表单时,比如在编辑了他们的显示名称之后,ModelState 无效。这是因为 UserName 属性上有一个私有设置器。然而这是设计使然,出于安全和语义的考虑,我不希望他们更改用户名,因此 setter 是私有的。但是,由于我已将Required 属性添加到属性中,因此它未设置而失败!
问题是模型绑定是否应该将此报告为验证错误?!由于该属性是私有的,因此我设计为不设置它,因此根据设计我不希望模型绑定器设置它,但我不想要验证错误。我认为它应该只对它可以设置的属性产生验证错误。
好的,到目前为止我已经提出了可能的解决方案..
公开财产。
如果我这样做,我会敞开心扉,允许为现有用户更改用户名。我必须在某处添加额外的逻辑才能捕捉到这一点,这不是很好。我还必须在操作方法上添加一个绑定排除,以阻止任何顽皮的人试图通过帖子设置它。
删除错误
我相信我可以从 ModelState 字典中删除错误,这在这种情况下会很好,但我认为这会引入一些代码异味,因为我必须为所有具有私有设置器的对象添加它。我可能会忘记!
针对界面强烈键入我的视图
我读过一些人将他们的视图绑定到他们模型的接口,这是模型视图接口到业务模型对象的王者。我喜欢这个想法,但我失去了自动绑定,需要在我的 web 层中复制我的模型对象及其构造函数,不确定吗?!一些信息在这里http://www.codethinked.com/post/2010/04/12/Easy-And-Safe-Model-Binding-In-ASPNET-MVC.aspx。
使用模型视图
这对我来说似乎并不干燥?!如果我没有适合的现有模型对象(例如我使用注册模型视图),我很乐意使用这些。
自定义模型绑定器
我的首选,但我不确定我知道我在做什么!!如果我能让活页夹只绑定到它可以设置的属性,那我会笑的!
人们怎么想?对上述选项的评论,任何其他解决方案,我的架构是否与我的架构格格不入?!
谢谢 :)