七、模型绑定

在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="" checked="checked" />
Type1
<input type="radio" name="Type" value="" 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="" id="Body" name="Body" rows=""></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>)
{
ModelError err=ModelState["Email"].Errors[];
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

MVC-03 控制器(4)的更多相关文章

  1. ASP.NET MVC 5 - 控制器

    MVC代表: 模型-视图-控制器 .MVC是一个架构良好并且易于测试和易于维护的开发模式.基于MVC模式的应用程序包含: · Models: 表示该应用程序的数据并使用验证逻辑来强制实施业务规则的数据 ...

  2. .NET/ASP.NET MVC Controller 控制器(IController控制器的创建过程)

    阅读目录: 1.开篇介绍 2.ASP.NETMVC IControllerFactory 控制器工厂接口 3.ASP.NETMVC DefaultControllerFactory 默认控制器工厂 4 ...

  3. MVC 在控制器中获取某个视图动态的HTML代码

    ASP.NET MVC 在控制器中获取某个视图动态的HTML代码   如果我们需要动态的用AJAX从服务器端获取HTML代码,拼接字符串是一种不好的方式,所以我们将HTML代码写在cshtml文件中, ...

  4. 三、ASP.NET MVC Controller 控制器(二:IController控制器的创建过程)

    阅读目录: 1.开篇介绍 2.ASP.NETMVC IControllerFactory 控制器工厂接口 3.ASP.NETMVC DefaultControllerFactory 默认控制器工厂 4 ...

  5. c#异步编程(三)—ASP.NET MVC 异步控制器及EF异步操作

    ASP.NET MVC 异步控制器及EF异步操作 异步控制器 ASP.NET MVC2后开始了对异步请求管道的支持,异步请求管道的作用是允许web服务器处理长时间运行的请求,比如 那些花费大量时间等待 ...

  6. [转]ASP.NET MVC 5 - 控制器

    MVC代表: 模型-视图-控制器 .MVC是一个架构良好并且易于测试和易于维护的开发模式.基于MVC模式的应用程序包含: · Models: 表示该应用程序的数据并使用验证逻辑来强制实施业务规则的数据 ...

  7. ASP.NET Core MVC 2.x 全面教程_ASP.NET Core MVC 03. 服务注册和管道

    ASP.NET Core MVC 2.x 全面教程_ASP.NET Core MVC 03. 服务注册和管道 语雀: https://www.yuque.com/yuejiangliu/dotnet/ ...

  8. Spring MVC(三)--控制器接受普通请求参数

    Spring MVC中控制器接受参数的类方式有以下几种: 普通参数:只要保证前端参数名称和传入控制器的参数名称一致即可,适合参数较少的情况: pojo类型:如果前端传的是一个pojo对象,只要保证参数 ...

  9. .NET/ASP.NET MVC Controller 控制器(深入解析控制器运行原理)

    阅读目录: 1.开篇介绍 2.ASP.NETMVC Controller 控制器的入口(Controller的执行流程) 3.ASP.NETMVC Controller 控制器的入口(Controll ...

  10. C# MVC ( 将控制器的实体类注册到视图 )

    (1)控制器  代码 using System; using System.Collections.Generic; using System.Linq; using System.Web; usin ...

随机推荐

  1. 使用HISTCONTROL强制history忽略某条特定命令

    http://www.linuxnote.org/mandatory-use-of-histcontrol-ignore-a-certain-specific-command-history.html ...

  2. C# 方法的可选参数、命名参数

    原文 http://www.cnblogs.com/lonelyxmas/admin/EditPosts.aspx?opt=1 C#方法的可选参数是.net 4.0最新提出的新的功能,对应简单的重载可 ...

  3. hdoj 1028 Ignatius and the Princess III(区间dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1028 思路分析:该问题要求求出某个整数能够被划分为多少个整数之和(如 4 = 2 + 2, 4 = 2 ...

  4. 人类科技的发展为什么会是加速度的(TRIZ方法再推荐)

    从人类的历史发展来看,近200年来的科技发展的成果超过了过去几千年中科技发展的成果,并且从短时间来看.这样的加速趋势也是很明显的,想想十年前和如今的对照,科技的发展确实是日新月异. 科技的发展固然有偶 ...

  5. SharePoint BCS

    1. 开启相关的服务:管理中心-->应用程序管理-->管理服务器上的服务 2.

  6. 【转】KVM/Installation

    [转]KVM/Installation Installation Pre-installation checklist Check that your CPU supports hardware vi ...

  7. BZOJ 3675: [Apio2014]序列分割( dp + 斜率优化 )

    WA了一版... 切点确定的话, 顺序是不会影响结果的..所以可以dp dp(i, k) = max(dp(j, k-1) + (sumn - sumi) * (sumi - sumj)) 然后斜率优 ...

  8. Eclipse环境问题集合

    1. XDoclet错误:Missing library: xdoclet-1.2.1.jar 从http://sourceforge.net/projects/xdoclet/files/xdocl ...

  9. JavaScript时钟实例

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  10. dlib库学习之一

    dlib库学习之一 1.介绍 跨平台 C++ 通用库 Dlib 发布 ,带来了一些新特性,包括概率 CKY 解析器,使用批量同步并行计算模型来创建应用的工具,新增两个聚合算法:中国低语 (Chines ...