zoukankan      html  css  js  c++  java
  • MVC-03 控制器(4)

    七、模型绑定

        在ASP.NET MVC中是通过模型绑定(Model Binding)达到解析客户端传来的数据。

    1.简单模型绑定

        当网页上有个窗体,且窗体内有个名为Username的输入字段,而Action的参数也定义了一个名为Username的参数,只要窗体的域名与Action方法上的参数名称一样,那么Action在被运行的时候,就会通过DefaultModelBinder类型将窗体或QueryString传来的数据进行处理,将原本传来的字符串数据转换成对应的.NET类型并传给Action方法的同名参数里。

        我们用个简单的例子来描述“简单模型绑定”的过程,请先参考以下动作方法的程序代码,Action名称为TestForm,它会通过简单模型绑定取得从客户端窗体传来的Username参数,最后会将该参数传入ViewData.Model让View使用。

            [HttpPost]
            public ActionResult TestForm(string Username)
            {
                ViewData.Model = Username;
                return View();
            }

         以下是相关的视图页面。

    <h2>TestForm</h2>
    
    <form method="post">
        <p>
            使用者名称:
            <input type="text" name="Username" />
        </p>
        <p>
            您输入的使用者名称为:@Model
        </p>
        <input type="submit" />
    </form>

        当表单提交后,你会发现窗体上的Username字段已经被成功传送到TestForm这个Action里,并且在Action里也成功接收到Username参数的信息,所以ViewData.Model才会有值,且View上的@Model才会正确显示文字在页面上。
        如果在VS2012中利用断点功能检查Action运行时是否真正接收到客户端表单传来的数据,应该可以发现表单信息的确已经被填入TestForm动作方法的Username参数里。

    2.使用FormCollection取得窗体信息

        除了通过简单模型绑定取得窗体传来的单栏信息外,还可以通过FormCollection一次取得整份窗体传来的信息。如下程序演示,只要设置一个FormCollection类型的参数,就可以取得所有从窗体传来的信息,这种用法如同使用以前的Request.Form一样。不过,在ASP.NET MVC里还是建议尽量不要使用Request.Form来取得窗体信息。

    除了通过简单模型绑定取得窗体传来的单个表单属性外,还可以通过(   )一次取得整份窗体传来的信息。

    A.ViewBag    B.ViewData    C.TempData    D.FormCollection

        我们将上一小节的演示重新改写Action的部分,代码如下。

            [HttpPost]
            public ActionResult TestForm(FormCollection form)
            {
                ViewData.Model = form["Username"];
                return View();
            }

    3.复杂模型绑定

        我们一样延续上一小节的演示,另外自定义一个名为UserForm的类别,且定义了三个属性(Properties),此时,Action若直接以UserForm类型来接收窗体信息也是没有问题的,只要表单域名称与UserForm类型中的属性名称一样,同样可以将客户端窗体信息自动绑定到form参数的同名属性上,代码如下。

            [HttpPost]
            public ActionResult TestForm(UserForm form)
            {
                ViewData.Model = form.Username;
                return View();
            }

        通过这种方式做模型绑定还有个好处,那就是我们可以利用VS2012的Intellisense快捷提示功能,帮助我们快速完成属性名称的输入。

        再举一个例子接收复杂模型绑定。假设窗体中有四个字段,分别为Type、Name、Email和Body,代码如下。

    <form method="post">
        Type
        <input type="radio" name="Type" value="1" checked="checked" />
        Type1
        <input type="radio" name="Type" value="2" checked="checked" />
        Type2
        <br />
           Name
           <input id="Name" name="Name" type="text" value="" />
           <br />
         
           Email
          <input id="Email" name="Email" type="text" value="" />
          <br />
    
           Body
           <textarea cols="20" id="Body" name="Body" rows="2"></textarea>
           <br />
    
           <input type="submit" />
    
    </form>

        数据模型与Action定义如下。

        public class GuestbookForm
        {
            public int Type { get; set; }
            public string Name { get; set; }
            public string Email { get; set; }
            public string Body { get; set; }
        }
            [HttpPost]
            public ActionResult TestForm(GuestbookForm gbook)
            {
                return View();
            }

        当客户端送出窗体到Save动作,ASP.NET MVC的DefaultModelBinder会很神奇地自动将字段信息映射到Action的gbook参数中。

    4.多个复杂模型绑定

    5.判断模型绑定的验证结果

        当Controller在模型绑定完成后,会得到一个完整的ModelState对象,这个对象将包括模型绑定的过程中收集到的各种信息,其中有模型绑定在输入验证后的状态、模型绑定过程中发生的异常、以及模型绑定时发生的异常,因此,当模型绑定发生输入验证失败时,会在Action里得到一个ModelState.IsValid为false的属性,此时,你就可以判断程序是否要继续运行下去,例如,原本想要通过模型绑定取得的信息新增至数据库,就可以改成新增错误消息到页面上。

        我们延续之前的演示,试着判断模型绑定成功与否。首先,声明一个含有模型验证属性的数据模型,并定义一个含有ModelState.IsValid判断条件的Action方法:

        public class GuestbookForm
        {
            [Required]
            public int Type { get; set; }
            [Required]
            public string Name { get; set; }
            [Required]
            public string Email { get; set; }
            [Required]
            public string Body { get; set; }
        }
            [HttpPost]
            public ActionResult TestForm(GuestbookForm gbook)
            {
                if (!ModelState.IsValid)
                {
                    //已验证出无效的模型绑定,有些字段不符合格式要求
                    return View();
                }
    
                //验证成功,此时可以将信息写入数据库
                //InsertIntoDB(gbook);
    
                return Redirect("/");
            }

        在视图的部分完全不用改写,在ModelState.IsValid这行设置一个断点,试着在只输入Type与Name字段的情况下输出窗体,当窗体接收信息时,你会发现ModelState.IsValid的值为false,如下图。

    6.模型绑定验证失败的错误详细信息

        除了可以在Action中验证模型绑定的验证状态外,在Action中还可以通过ModelState属性取得ASP.NET MVC内建的验证失败错误消息。

        若要取得在模型绑定的过程中总共有多少属性会被绑定,可以通过以下程序取得:

    ModelState.Count

        若要取得特定属性在绑定过程中是否出现错误,可用以下程序取得:

    if(ModelState["Email"].Errors.Count>0)
    {
        //...
    }

        若要取得特定属性在绑定过程中出现的第一个错误,以及其错误消息或Exception对象,可用以下程序取得:

    if(ModelState["Email"].Errors.Count>0)
    {
        ModelError err=ModelState["Email"].Errors[0];
        var errMsg=err.ErrorMessage;
        var errExp=err.Exception;
    }

        除了可以取得模型绑定过程中内建的验证失败信息外,还可以自行增加模型绑定验证失败的信息。

            [HttpPost]
            public ActionResult TestForm(GuestbookForm gbook)
            {
                if (!ModelState.IsValid)
                {
                    //已验证出无效的模型绑定,有些字段不符合格式要求
    
                    if (gbook.Email == null)
                        ModelState.AddModelError("Email", "请输入Email字段");
    
                    return View();
                }
    
                //验证成功,此时可以将信息写入数据库
                //InsertIntoDB(gbook);
    
                return Redirect("/");
            }

    7.清空模型绑定状态

        在Action里除了得到这些模型绑定的详细信息外,ModelState对象里的信息也一样会传送到View里,如果希望模型绑定状态(ModelState)不要传送到View里,还可以将模型绑定的所有状态清空,让View页面上的强类型信息不受模型绑定状态的影响,代码如下。

            [HttpPost]
            public ActionResult TestForm(GuestbookForm gbook)
            {
                if (!ModelState.IsValid)
                {
                    //已验证出无效的模型绑定,有些字段不符合格式要求
    
                    //清空模型绑定状态
                    ModelState.Clear();
    
                    return View();
                }
    
                //验证成功,此时可以将信息写入数据库
                //InsertIntoDB(gbook);
    
                return Redirect("/");
            }

    8.使用Bind属性限制可被更新的数据模型属性

        复杂模型绑定的验证技巧在实际中经常使用也非常方便,但有一个很明显的限制,那就是模型在做绑定的时候,是在Action运行时就完成了,而且不管Model有多少字段,只要客户端有窗体过来就会自动绑定,看来方便,但实际上是有安全风险的。

        因为客户端的表单域非常容易被窜改,如果黑客企图从窗体塞如一些额外的表单域,只要猜到正确的属性名称,就可以通过ASP.NET MVC的模型绑定功能自动将数据绑定到特定对象的同名属性里。

        举个实际的例子来说,假设你有个数据模型名为Member,其属性定义如下,其中LastLoginTime属性代表的是“上次登录时间”。

        public class Member
        {
            public int Id { get; set; }
            public string Username { get; set; }
            public string Password { get; set; }
            public DateTime? LastLoginTime { get; set; }
        }

        而你的客户端窗体上只有让用户输入Username与Password而已,所以当你使用模型绑定的方式传入Member信息后,会预期LastLoginTime字段应该不会绑定到任何信息,而且该字段传入之后的同名属性值应该为null才对。程序代码如下:

            public ActionResult UpdateProfile(Member member)
            {
                //TODO:更新数据库中的Member信息
    
                return View();
            }

        但如果黑客这时窜改了客户端窗体,多塞一个LastLoginTime字段上去,并设置任意时间,那么你数据库中的这条信息,其LastLoginTime字段可能就会被用户任意窜改,如此一来,ASP.NET MVC程序就会有风险,因此不得不小心。
       此时,可通过ASP.NET MVC内建的Bind属性(Attribute)并套用在该数据模型的参数上,明确声明有哪些字段可以被自动绑定进来,或是哪些字段该被排除在自动绑定的名单外。以下演示程序就是声明Member参数在自动绑定时要排除LastLoginTime字段的信息:

            public ActionResult UpdateProfile([Bind(Exclude="LastLoginTime")]Member member)
            {
                //TODO:更新数据库中的Member信息
    
                return View();
            }

        如果你想明确指明“只有”哪些字段需要绑定,可以使用Include具名参数。

            public ActionResult UpdateProfile([Bind(Include="Password")]Member member)
            {
                //TODO:更新数据库中的Member信息
    
                return View();
            }

    通过ASP.NET MVC内建的( )属性,可以明确声明哪些字段可以被自动绑定进来,或是哪些字段该被排除在自动绑定的名单外。

    A.Include    B.Exclude    C.Bind    D.Filter   

          如果你不希望在每个Action的参数都套用Bind属性的话,也可以套用在数据模型声明定义的地方,这样一来,整个项目的模型都不需要额外的声明了。

        [Bind(Include="Username,Password")]
        public class Member
        {
            public int Id { get; set; }
            public string Username { get; set; }
            public string Password { get; set; }
            public DateTime? LastLoginTime { get; set; }
        }

    9.使用UpdataeModel与TryUpdateModel

  • 相关阅读:
    linux 下查看目录所占用空间大小 du命令功能
    nginx 的启动 停止和重启
    excel vba
    过程中遇到的问题
    Java python BDA文件上传下载项目遇到的问题
    java login小方法集合
    python 初学
    python 从0学起
    python交互的几种方式
    模块与包概念与调用
  • 原文地址:https://www.cnblogs.com/meetyy/p/3460144.html
Copyright © 2011-2022 走看看