zoukankan      html  css  js  c++  java
  • 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

     
    分类: C#
     
    好文要顶 已关注 收藏该文  
  • 相关阅读:
    LINQ to SQL 模拟实现 ROW_NUMBER() OVER(ORDER BY ...) 的功能
    TCP粘包、拆包与通信协议
    Decoder和Encoder
    Future 和 ChannelFuture
    通道Channel
    通道初始化器ChannelInitializer
    数据处理器ChannelHandler
    通道配置项 ChannelOption
    ChannelPipeline
    启动配置类Bootstrap 和 ServerBootstrap
  • 原文地址:https://www.cnblogs.com/0to9/p/5200865.html
Copyright © 2011-2022 走看看