MVC 表單提交【轉】


【轉自】:http://www.cnblogs.com/dengdl/archive/2011/07/14/2106849.html

在做Asp.Net MVC項目中,都知道View負責頁面展示數據或者提供頁面收集數據,而所展示的數據或者收集的數據都是從Controller的Action中獲取或提交到Controller的Action。

這里的數據,可能是基礎類型,或者是Model,或者是Model的部分內容,或者是集合比如List或Dictionary。

數據從View傳遞到Controller的Action時,有幾種方式,RouteData(url中的路由數據),QueryString(http get的查詢參數如?page=2),Forms(表單post的數據), 或者ajax交互的json數據。

而在Controller的action中,常常希望獲得這些View傳遞的數據,並且最好能綁定到Action希望的類型,這些類型可能是Action的基礎類型參數,或者需要更新的Model,或者只更新Model的部分內容,或者是一些集合的結構如List或Dictionary。

古董方法:

[AcceptVerbs(HttpVerbs.Post)]

publicActionResultCreate()

{

    Reciperecipe = newRecipe();

    recipe.Name = Request.Form["Name"];

    

    // ...

    

   returnView();

}

前進一步:

publicActionResultCreate(FormCollectionvalues)

{

    Reciperecipe = newRecipe();

    recipe.Name = values["Name"];      

            

    // ...

            

   returnView();

}

神奇的DefaultModelBinder:

Asp.Net Mvc內建功能(DefaultModelBinder)可以實現簡單類型、復雜類型、集合類型,以及字典類型的自動綁定。 

1. 簡單類型

這里,我們將下面這個Book類稱為簡單類型:

public class Book
    {
        public int BookId { get; set; }
        public string BookName { get; set; }
        public string Author { get; set; }
        public DateTime PublishedDate { get; set; }
    }


假設現在需要實現添加Book的功能,那么在BookController中,會定義如下的Action:

[AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create(Book book) {
            //TO DO
            //Insert book into Database
            return RedirectToAction("Index");
        }

現在的問題便是,在View中如何命名TextBox來達到自動綁定,如下:

<div>
        <%using (Html.BeginForm("Create", "Book")) { %>
        <div>
            Book Name: <%=Html.TextBox("BookName")%>
        </div>
        <div>
            Author: <%=Html.TextBox("Author")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("PublishedDate")%>
        </div>
        <div>
            <input type="submit" id="submit" name="submit" value="submit" />
        </div>
        <%} %>
    </div>


注意TextBox的name必須是對應綁定類型的PropertyName(不區分大小寫)。 這樣,頁面表單submit后,我們便可以在BookController的“Create” Action中得到自動綁定的book對象。這里,Asp.Net Mvc還支持在TextBox的name中加上變量名稱前綴的情形:

<div>
        <%using (Html.BeginForm("Create", "Book")) { %>
        <div>
            Book Name: <%=Html.TextBox("book.BookName")%>
        </div>
        <div>
            Author: <%=Html.TextBox("book.Author")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("book.PublishedDate")%>
        </div>
        <div>
            <input type="submit" id="submit" name="submit" value="submit" />
        </div>
        <%} %>
    </div>


需要注意的是:
1)前綴"book"必須與Action中接收的Book參數名一致
2)如果加了前綴,那么所有的都要加

2. 復雜類型

現在對Book類作些許改動,並引入Author類:

public class Book
    {
        public int BookId { get; set; }
        public string BookName { get; set; }
        public Author Author { get; set; }
        public DateTime PublishedDate { get; set; }
    }

    public class Author {
        public int AuthorId { get; set; }
        public string AuthorName { get; set; }
        public string Nation { get; set; }
    }


這里,將改動后的Book類稱為復雜類。這時,Book類多了一個對Author類的引用。現在,保持BookController中的"Create" Action不變,來看View中的TextBox改如何命名以實現Book類型的自動綁定:

<div>
        <%using (Html.BeginForm("Create", "Book")) { %>
        <div>
            Book Name: <%=Html.TextBox("BookName")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("PublishedDate")%>
        </div>
        <div>
            Author's Name: <%=Html.TextBox("Author.AuthorName")%>
        </div>
        <div>
            Author's Nation: <%=Html.TextBox("Author.Nation")%>
        </div>
        <div>
            <input type="submit" id="submit" name="submit" value="submit" />
        </div>
        <%} %>
    </div>


OK,測試通過,想必你也知道命名規則了,要綁定Book類型中的Author類型,必須加上"Author."的前綴。
如果你喜歡,你還可以在所有TextBox名稱前面再加"book."的前綴。

3. 集合類型

為避免問題復雜化,我們用回原來的簡單Book類型:

public class Book
    {
        public int BookId { get; set; }
        public string BookName { get; set; }
        public string Author { get; set; }
        public DateTime PublishedDate { get; set; }
    }


現在,把BookController的"Create" Action改為接收IList<Book>的參數:

[AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create(IList<Book> books) {
            //TO DO
            //Insert book into Database
            return RedirectToAction("Index");
        }


然后,在View中運用以下命名規則,以自動綁定IList<book>類型,

<div>
        <%using (Html.BeginForm("Create", "Book")) { %>
        <div>
            Book Name: <%=Html.TextBox("books[0].BookName")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("books[0].PublishedDate")%>
        </div>
        <div>
            Author's Name: <%=Html.TextBox("books[0].Author")%>
        </div>
        <div>
            Book Name: <%=Html.TextBox("books[1].BookName")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("books[1].PublishedDate")%>
        </div>
        <div>
            Author's Name: <%=Html.TextBox("books[1].Author")%>
        </div>
        <div>
            <input type="submit" id="submit" name="submit" value="submit" />
        </div>
        <%} %>
    </div>


可以看到如下規則:Action的變量名"books"加上中括號和索引作為前綴,索引必須從0開始,並且必須連續。
通過此命名規則,可以綁定任意集合類型:IList<Book>, ICollection<Book>, IEnumerable<Book>, List<Book>, Book[]等。

4. 字典類型

仍以簡單Book類型為例,現在將"Create" Action改為接收IDictionary<Book>類型,

[AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create(IDictionary<string, Book> books) {
            //TO DO
            //Insert book into Database
            return RedirectToAction("Index");
        }


相應的,View中的命名如下:

<div>
        <%using (Html.BeginForm("Create", "Book")) { %>
        <div>
            Book SN: <%=Html.TextBox("books[0].Key") %>
        </div>
        <div>
            Book Name: <%=Html.TextBox("books[0].Value.BookName")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("books[0].Value.PublishedDate")%>
        </div>
        <div>
            Author's Name: <%=Html.TextBox("books[0].Value.Author")%>
        </div>
        <div>
            Book SN: <%=Html.TextBox("books[1].Key") %>
        </div>
        <div>
            Book Name: <%=Html.TextBox("books[1].Value.BookName")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("books[1].Value.PublishedDate")%>
        </div>
        <div>
            Author's Name: <%=Html.TextBox("books[1].Value.Author")%>
        </div>
        <div>
            <input type="submit" id="submit" name="submit" value="submit" />
        </div>
        <%} %>
    </div>


可以看出,對於IDictioinary<Book>類型,在命名規則上,相比於集合類型,多了"Key"和"Value”的字眼。另,此規則也適用於Action參數類型為Dictionary<Book>的情形。

簡單類型、復雜類型、集合類型,以及字典類型 還能混合着用,而且綁定的數據來源也不局限於Form提交的表達數據,還可以是RouteData(url中的路由數據),QueryString(http get的查詢參數如?page=2),或者ajax交互的json數據。

所以,只要我們遵循一定的命名規則,靈活的運用DefaultModelBinder 就可以輕松實現各種類型的自動綁定了。 

BindAttribute:

DefaultModelBinder 已經很強大了,而在有些時候BindAttribute則會為你錦上添花。

BindAttribute中有三個重要的成員:string Exclude, string Include, string Prefix。
"Exclude"用於指定要排除的Property,"Include"用於指定包含在內的Property,"Prefix"用於指定前綴。

看個例子,

Book類:


Create Action:

 
對應的View:

先說,"Prefix"吧,默認情況下,DefaultModelBinder可以自動識別以Action的參數名命名的前綴,也就是說,在上述View中,給每個TextBox的名稱都加一個"book“前綴,不需要在做任何改動,在Action中仍可得到Book的實例。

加上前綴”book"的View:


這時,如果我們加的前綴不是"book",那么就需要BindAttribute的協助了,
假設,我們加上一個"b"的前綴:


那么,為了在Action中得到相應的Book實例,需要在Action的Book參數上應用BindAttribute:

 
現在來看"Exclude"和"Include",其實這兩個東西一次應用一個就可以了。現在我希望Binding的時候將"BookName"和"Author"都排除(當然,這里這樣做沒什么意義)。出於簡化問題的考慮,View去掉TextBox名稱的前綴:

然后,將Action改為:


默認情況下,多個Property用逗號隔開。

BindAttribute出了可以應用在Action的參數上外,還可以應用在Model類定義中:


如果在Model類定義中,和在Action的參數上都應用了BindAttribute,那么則會取兩者的交集。個人認為,還是應盡量避免它們打架為妙。

所以,BindAttribute 對於前綴的使用及部分Model的綁定很有用。

TryUpdateModel/UpdateModel  

 Mvc中經常遇到的一個典型用例是View通過From或者Ajax提交數據更新相應的Model,或者更新Model的部分內容,並且需要檢查提交的數據對於Model的數據約束是否有效。

這個時候TryUpdateModel就有用武之地了,它可以設置需要更新模型屬性列表的(白名單)或要排除屬性的列表(黑名單) 先看更新相關模型的所有屬性:

publicActionResult Edit(int id, FormCollection collection)

{

    var oldData = _r.GetSingleData(id);

 

    if(TryUpdateModel(oldData, collection.AllKeys))

    {

        _r.Save();

    }

}

更新除ID,Name外的Model屬性:

publicActionResult Edit(Guid id, FormCollection collection)

{

    var oldData = _r.GetSingleData(id);

 

    if(TryUpdateModel(oldData,"", collection.AllKeys,newstring[]{"ID","Name"}))

    {

        _r.Save();

    }

}

publicActionResult Save()

{

    Customer customer =newCustomer();

   try   {

        UpdateModel(customer,new[] {"Name","Email",

           "Phone","Deposit"});

       return RedirectToAction("...");

    }

   catch(InvalidOperationException)

    {

       returnView(customer);

    }

}

UpdateModel 和TryUpdateModel 有許多重載形式以供多種靈活的運用,而且它還將自動檢查模型綁定數據是否有錯誤,如果有錯誤將自動添加到ModelState 並在頁面作出提示。真是個好用的寶貝。

自定義ModelBinder

這是一個終極BOSS,由你自己親手打造,一般來說,如果前面的武器不能解決的,都可以使用這個干掉,當然一般問題前面的方法都可以解決。

要想修煉custom model binder, 自己google.

 

參考:

http://blog.csdn.net/aspgreener/article/details/4986190 

http://blog.csdn.net/aspgreener/article/details/4986379 

http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx 

http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx 

http://demo.tc/Post/655 

http://msdn.microsoft.com/zh-cn/library/system.web.mvc.controller.tryupdatemodel.aspx 

http://davidhayden.com/blog/dave/archive/2008/09/08/ASPNETMVCUpdateModelTryUpdateModelDataBinding.aspx 

http://www.joe-stevens.com/2010/02/17/asp-net-mvc-using-controller-updatemodel-when-using-a-viewmodel/ 

http://kb.cnblogs.com/page/69750/ 

http://www.cnblogs.com/ldp615/archive/2010/07/30/SensitiveWordsFilterModelBinder.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM