The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

ASP.NET MVC 開發心得分享 (6):小心使用 FormCollection

ASP.NET MVC 的 Model Binder 實在是超級好用的東西,不過卻有個需要小心使用的 FormCollection 型別,他感覺上好像也是利用 Model Binder 技術自動繫結資料,且會讓你誤以為使用 FormCollection 時在 ModelState 中也會有資料,但事實上卻沒有。

我舉一個簡單的例子來說,以下是一個 View 中的文字輸入框:

<%= Html.TextBox("Email")%>

當按下 Submit 後,會傳到 Controller 中的 Action 裡,例如:

public ActionResult Login(string Email)
{
    return View();
}

這裡的 Email 參數就是透過 Model Binder 繫結(binding)進來的,然後 View 會再次呈現,且 Email 文字輸入框會被放入 Submit 之前所輸入的資料,而重點是透過 Html Helper 所繫結(binding)進來的資料有以下套用的順序:

  1. 透過 ModelStateValue 中的同名屬性取得值 ( 最高順位 )
    • ModelStateValue 包括幾個來源:
      • 從 Action 傳來的 Model 強型別資料
      • 透過 Model Binder 傳入且成功綁定(Binding)的資料
  2. 透過 Html Helper 明確指定所取得的值
    • 當 ModelStateValue 找不到資料時,就套用 Html Helper 明確指定的值
    • 例如:<%= Html.TextBox("Email", "someone@example.com")%>
  3. 透過同名的 ViewData 取得值
    • 當 ModelStateValue 找不到資料且 Html Helper 也沒有明確指定預設值時,就會改從 ViewData 取得資料。
    • 例如:ViewData["Email"] 如果不為 null 就會將這個資料輸入到 Email 文字輸入框裡

以上順序非常重要,這些資訊是我追查 ASP.NET MVC RTM 原始碼得來的心得。

其中最高順位的 ModelStateValue 就包括透過 Model Binder 繫結到的資料,而當你在 Action 方法中套用了 FormCollection 之後,就會讓沒有被 Model Binder 繫結到的欄位全部丟入 FormCollection 集合,而 FormCollection 並非 Model Binder 的範疇,意思也就是說只要有在 FormCollection 中出現的欄位,在 ModelStateValue 裡都將會是空的。

所以當你的 Action 方法改成如下的程式碼時:

public ActionResult Login(FormCollection collection)
{
    return View();
}

你原本在 View 中輸入的資料,在 Submit 出去後如果再回到 View 中將不會看到你上一頁輸入的值!

但如果你這樣寫:

public ActionResult Login(FormCollection collection)
{
    ViewData["Email"] = collection["Email"];
    return View();
}

這時透過 Html Helper 輸出的欄位就會有預設值了,當然這是不智的作法,最好的開發方式還是善用 Model Binder 進行設計,才能得到比較容易維護的程式。

相關連結