2013-10-10 51 views
4

我已經創建了我的數據庫的ADO.NET模型。 創建一個帶有CRUD的新控制器(實體框架並使用我創建的ADO.NET實體模型)。繞過控制器中的模型驗證?

在我的數據庫中有一個簡單的用戶表。表中的密碼行將保存用SimpleCrypto(PBKDF2)加密的用戶密碼。

在我的ADO.NET Users.cs模型我已經添加以下驗證:

[Required] 
[DataType(DataType.Password)] 
[StringLength(20, MinimumLength = 6)] 
[Display(Name = "Password")] 
public string Password { get; set; } 

這與jQuery與驗證的瀏覽器。 但在我的控制器中,我正在加密密碼,然後密碼字符串的長度將超過20個字符。

var crypto = new SimpleCrypto.PBKDF2(); 
var encryptedPass = crypto.Compute(user.Password); 

user.Password = encryptedPass; 
user.PasswordSalt = crypto.Salt; 

_db.Users.Add(user); 
_db.SaveChanges(); 

這使我和「驗證失敗的一個或多個實體。」 - 錯誤。

我可以將用戶複製到一個「var newUser」,然後設置所有屬性,但在這種情況下沒有更簡單的方法繞過模型驗證嗎?

編輯:如果我刪除模型中的密碼道具驗證然後一切正常。所以這是驗證,給我的錯誤,因爲我把密碼從6-20長度字符改爲+100長度字符,因爲控制器中的加密。

編輯:完成控制器部分插入到這個問題。

[HttpPost] 
public ActionResult Create(Users user) 
{ 
    if (!ModelState.IsValid) 
    { 
     return View(); 
    } 
    if (_db.Users.FirstOrDefault(u => u.Email == user.Email) != null) 
    { 
     ModelState.AddModelError("", "User already exists in database!"); 
     return View(); 
    } 

    var crypto = new SimpleCrypto.PBKDF2(); 
    var encryptedPass = crypto.Compute(user.Password); 

    user.Password = encryptedPass; 
    user.PasswordSalt = crypto.Salt; 

    _db.Users.Add(user); 
    _db.SaveChanges(); 

    return RedirectToAction("Index", "User"); 
} 
+1

我編輯了自己的冠軍。請參閱:「[應該在其標題中包含」標籤「](http://meta.stackexchange.com/questions/19190/)」,其中的共識是「不,他們不應該」。 –

回答

2

如果我理解正確,你有一個密碼字段的數據庫表。
根據你的模型這個密碼字段爲20個字符長

[StringLength(20, MinimumLength = 6)] 

而且要插入的值大於20個字符。
如果實體框架沒有阻止你,那麼你會得到一個數據庫錯誤(實體框架不知道你的數據模型和數據庫之間是不匹配的,它不想冒插入通過的風險)

但是,我認爲你需要在視圖模型上指定clientSide驗證規則,而不是數據庫上的長度約束。
我希望你明白爲什麼這是一個令人困惑的設置。

我的建議是要麼分裂您的視圖模型和模型,以便您可以發佈視圖模型與最大長度20的未加密的口令如果發現太多,你可以轉換成模型密碼長度爲100

麻煩您可以創建一個未映射的密碼屬性,您可以在發佈html時進行設置,並將其轉換爲控制器中的密碼屬性。
你的類可以是這樣的:

public class RegisterModel 
{ 
    [Required] 
    public string UserName { get; set; } 

    [Required] 
    [NotMapped] 
    [StringLength(20, MinimumLength = 6)] 
    [Display(Name = "Password")] 
    public string PlainTextPassword { get; set; } 

    [Required] 
    [StringLength(300)]//This is optional 
    [DataType(DataType.Password)] 
    public string Password { get; set; } 
} 
+0

是的,你正確理解我。但是,密碼字段的實際最大長度是300.我想我需要閱讀關於ViewModels。謝謝 – user1281991

+0

你不需要viewmodel本身。 stringLength屬性觸發實體框架驗證以及客戶端驗證。因此,如果您在提交更改時說StringLength 20客戶機端的閥值觸發器以及實體框架檢查。通過使用未映射的屬性(EF不使用),您可以在PlainTextPassword上觸發clientSide驗證,但將實體框架保留在Password上。如果你願意,你可以添加Stringlength 300。 – Kristof

+0

謝謝!你讓我明白:)它的工作原理!謝謝 – user1281991

3

你說你的密碼加密發生在控制器中。在這種情況下,你是否應該在驗證後加密?例如:

public ActionResult SomeControllerAction(UserViewModel user) 
{ 
    if (!ModelState.IsValid) 
    { 
     // at this point the human readable (and assuming < 20 length) password 
     // would be getting validated 
     return View(user); 
    } 

    // now when you're writing the record to the DB, encrypt the password 
    var crypto = new SimpleCrypto.PBKDF2(); 
    var encryptedPass = crypto.Compute(user.Password); 

    user.Password = encryptedPass; 
    user.PasswordSalt = crypto.Salt; 

    _db.Users.Add(user); 
    _db.SaveChanges(); 

    // return or redirect to whatever route you need 
} 

如果你想專門控制您的驗證,然後嘗試實現您的視圖模型類IValidatableObject並在這裏進行驗證,而不是通過屬性。例如:

public class UserViewModel : IValidatableObject 
{ 
    public string Username { get; set; } 
    public string Password { get; set; } 

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
     // validate the unencrypted password's length to be < 20 
     if (this.Password.Length > 20) 
     { 
      yield return new ValidationResult("Password too long!"); 
     } 
    }   
} 
+0

嗨,謝謝你。我是MVC的新手。我在上面的問題中粘貼了控制器部分。 – user1281991

+0

@ user1281991沒問題,看起來你的驗證在數據庫級別失敗 - 你正在加密的密碼長度比你的'User.Password'數據庫表列可以合理保存的時間長。這個項目是第一個代碼還是數據庫? –

+0

不能是數據庫中導致問題的長度。數據庫中的密碼長度設置爲300. 嘗試在Visual Studio中進行調試,並輸入一個隨機密碼,並將其加密爲:「HS90iFjoivSHcc1D6Ynt6liYKr + VKpcOu1nPwUhe5qPqGbjRUfEzff93VdsicETbFOnNmyaxyc6VrVimiQGNww ==」,這就像100個字符我猜? 如果我刪除我的模型中的密碼驗證,那麼它的工作原理?沒有錯誤發生,並且所有商品都在DB – user1281991

8

這是ViewModels進入遊戲的地方。您應該創建一個模型,然後將其傳遞給視圖並將其映射回域模型。

視圖模型:

public class RegisterModel 
{ 
    [Required] 
    public string UserName { get; set; } 

    [Required] 
    [DataType(DataType.Password)] 
    [StringLength(20, MinimumLength = 6)] 
    [Display(Name = "Password")] 
    public string Password { get; set; } 
} 

域模型(你目前User模型):

public class User 
{ 
    // other properties.. 

    [Required] 
    public string Password { get; set; } 
} 

你可以在你的控制器使用這些模型是這樣的:

GET行動:

public ActionResult Register() 
{ 
    var registerModel = new RegisterModel(); 
    return View(registerModel) 
} 

有了這樣的觀點:

@model RegisterModel 

@Html.LabelFor(model => model.UserName) 
@Html.TextBoxFor(model => model.UserName) 
@Html.ValidationMessageFor(model => model.UserName) 

@Html.LabelFor(model => model.Password) 
@Html.PasswordFor(model => model.Password) 
@Html.ValidationMessageFor(model => model.Password) 

而且POST行動:

[HttpPost] 
public ActionResult Register(RegisterModel registerModel) 
{ 
    // Map RegisterModel to a User model.  
    var user = new User 
        { 
         UserName = registerModel.UserName, 
         Password = registerModel.Password // Do the hasing here for example. 
        }; 
    db.Users.Add(user); 
    db.SaveChanges();       
} 
+0

中正常銷售。感謝此。那麼愚蠢的問題。我如何將RegisterModel「視圖」和「用戶」模型「綁定」到控制器上? – user1281991

+1

抱歉劫持這個問題,但是你不應該將兩種不同的模型綁定到你的視圖和控制器上。你會將'RegisterModel'傳遞給你的視圖,並且你期待'RegisterModel'返回 - 由於這個原因,MVC有它自己的綁定處理程序。我建議你做一些更多的閱讀:http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-3 –

+0

@ user1281991我更新了我的anwer與兩個控制器的例子行動和觀點。 –