我正在使用ASP.NET MVC 2.0,並試圖利用模型綁定在我的控制器中以及modelstate驗證。不過,我已經提出了一個問題,並希望與這裏的人分享,看看你的想法。ModelState在ASP.NET MVC中爲模型上的私有屬性報告爲無效2
好吧,我有我的清潔用戶的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有意義,但是是我的問題的關鍵,所以我想在此突出顯示它。
然後我有一個'哥們類的定義元數據驗證我的User類...
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; }
}
}
}
然後在我的網絡層我想,讓我的用戶是能夠編輯這個對象。所以我在我的配置文件控制器中有以下兩種操作方法。
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。然而,這是設計的,對於安全性和語義我並不希望他們改變他們的用戶名,所以setter是私人的。但是,由於我已將必需屬性添加到屬性中,因此未設置,因此它失敗!
問題是模型綁定是否應該報告這是驗證錯誤?由於該屬性是私人的,因此我設計了它不被設置,因此通過設計我不希望模型綁定器設置它,但我不想要驗證錯誤。我認爲它應該只會產生可以設置的屬性的驗證錯誤。
好了,所以可能的解決方案我想出了迄今爲止..
使屬性公開。
如果我這樣做,我打開自己允許更改現有用戶的用戶名。我不得不在某處添加額外的邏輯來捕捉這個,並不是很好。我還必須在操作方法上添加一個Bind Exclude,以阻止任何頑皮的人試圖通過帖子進行設置。
刪除錯誤
我相信我可以從ModelState中字典中刪除錯誤,這將是在這個場合很好,但我認爲這將介紹一些代碼味道,因爲我將不得不添加此對所有我的對象有私人設置者。我可能會忘記!
強類型我認爲對一個接口
我讀過,有些人他們的觀點結合到他們的模型的接口,這是一個模型視圖界面到業務模型對象的國王。我喜歡這個想法,但是我放棄了自動綁定,並且需要將我的模型對象與他們的構造函數複製到我的Web層中,不確定?這裏的一些信息http://www.codethinked.com/post/2010/04/12/Easy-And-Safe-Model-Binding-In-ASPNET-MVC.aspx。
使用模型瀏覽次數
這只是對我而言似乎幹?如果我沒有適合的現有模型對象(例如,我使用註冊模型視圖),我很樂意使用這些對象。
CustomModelBinder
我的優先選擇,但我不知道,我知道我在做什麼!如果我只能讓活頁夾綁定到它可以設置的屬性,那麼我會笑!
人們認爲什麼?對上述選項的評論,還有其他的解決方案,我剛剛脫離了我的架構?!
謝謝:)
感謝鏈接jfar,這是一個非常有趣的閱讀。 我不認爲我在談論模型與輸入驗證。我同意整個模型應該被驗證。不過,我認爲我得到的問題是ModelSate.isValid推動你讓所有必需的屬性都有公共設置者。所以真的它只適用於ModelViews。 我不認爲在我的模型上需要使用私有setter的屬性是不可能的編碼方案,更多的是安全問題。我通過構造函數設置它們。 TryUpdateModel可能是我會看的一個選項。 – j3ffb 2010-07-13 11:37:58
@ j3ffb當然你是。在MVC 1中,如果你不能綁定一個屬性,一切都會好的。現在它不是。 – jfar 2010-07-13 13:12:19
無論哪種方式,它似乎已被設計爲與所有屬性都是公共的ModelView一起使用。 – j3ffb 2010-07-13 18:32:36