2010-07-12 61 views
1

我正在使用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

我的優先選擇,但我不知道,我知道我在做什麼!如果我只能讓活頁夾綁定到它可以設置的屬性,那麼我會笑!

人們認爲什麼?對上述選項的評論,還有其他的解決方案,我剛剛脫離了我的架構?!

謝謝:)

回答

0

jfar張貼了一個很好的鏈接到Brad Wilson的帖子Brad的評論...

你仍然可以做局部的修改,但 你不能做部分驗證任何 更多。因此,如果您排除 與[必需的] 屬性的綁定,則驗證將失敗。 您有幾種選擇來解決 此:

  • 使用這恰好反映了表單數據

    視圖模型
  • 預填調用 前[必需的]但是數據綁定字段(試試)的UpdateModel使得 驗證將成功(即使 你不打算與 做任何數據)

  • 允許驗證錯誤OC cur,然後從 ModelState中刪除它們,驗證完成後, 因爲它們不合適的錯誤。

我的情況似乎融入「部分編輯」的情況下,在這裏我不想某些字段進行更新。

我會研究這些解決方案。

2

我設計爲它不會設置,因此在設計我並不指望模型綁定進行設置,但我不希望驗證錯誤。我認爲應該只生產驗證錯誤的,它可以設置屬性

瞭解更多關於這個設計決定在這裏:

http://bradwilson.typepad.com/blog/2010/01/input-validation-vs-model-validation-in-aspnet-mvc.html

有趣的是大多數人抱怨與你的抱怨完全相反。 ;)

你基本上告訴系統應該總是設置一些不能設置的東西。所以我不會說MVC工作不正確或類似的東西。你只是編碼一個不可能的場景。


總體而言,您只是達到了metadatabuddy技術的痛點。主要是需要對新的和編輯方案進行不同的驗證。

」如果我這樣做,我打開自己,允許更改現有用戶的用戶名,我將不得不添加額外的邏輯來捕捉這個,不是很好,我還必須添加一個綁定排除在行動方法,以阻止任何淘氣的人試圖設置它通過一個職位。

恕我直言,你的暴飲暴食這些代碼的變化。您將爲單個方法調用添加一個簡單的字符串。最重要的是什麼?我會在這裏採取務實的態度。

+0

感謝鏈接jfar,這是一個非常有趣的閱讀。 我不認爲我在談論模型與輸入驗證。我同意整個模型應該被驗證。不過,我認爲我得到的問題是ModelSate.isValid推動你讓所有必需的屬性都有公共設置者。所以真的它只適用於ModelViews。 我不認爲在我的模型上需要使用私有setter的屬性是不可能的編碼方案,更多的是安全問題。我通過構造函數設置它們。 TryUpdateModel可能是我會看的一個選項。 – j3ffb 2010-07-13 11:37:58

+0

@ j3ffb當然你是。在MVC 1中,如果你不能綁定一個屬性,一切都會好的。現在它不是。 – jfar 2010-07-13 13:12:19

+0

無論哪種方式,它似乎已被設計爲與所有屬性都是公共的ModelView一起使用。 – j3ffb 2010-07-13 18:32:36

2

我會使用視圖模型,因爲它最適合這項工作。不要認爲DRY意味着你不能在兩個對象上重複屬性,認爲它是「不重複邏輯或在兩個地方持久保存相同的數據」。在這種情況下,處理模型綁定的語義與您的領域模型不匹配,因此您需要一種翻譯它的方法。

+0

謝謝瑞恩,我明白你的意思了,我想我只是不喜歡重複我的對象爲了隱藏一些屬性的想法,只是覺得這是什麼接口;) – j3ffb 2010-07-13 10:46:31

相關問題