转载自

Leo‘s Blog

看到很多ASP.NET MVC项目还在从request.querystring或者formContext里面获取数据,这实在是非常落后的做法。也有的项目建了大量的自定义的modelbinder,以为很牛,实际上也落后的很。

ASP.NET MVC提供了IModelBinder的默认实现,这个实现的类就叫DefaultModelBinder。我们在写代码的时候,几乎感觉不到这个类的存在,因为这个类自动将request信息解析成action参数。本文将向大家展示这个类是多么强大,可以拯救大量的代码。

先看个例子。假如有如下表单,用于编辑用户信息以及该用户的时间表。在这个例子中,我要利用DefaultModelBinder自动将整个表单数据解析成复杂实体类的实例。一行手工解析的C#代码都不用写。

对应的controller的代码如下,很简单:

 1 public class DemoController : PublicControllerBase
2 {
3 public ActionResult UserEditor()
4 {
5 return View();
6 }
7
8 [HttpPost]
9 public string SaveUser(DemoUser user)
10 {
11 var result = string.Empty;
12 if (user != null)
13 {
14 result = Serializer.ToJson(user);
15 }
16 return result;
17 }
18 }

相关的实体类的定义,也很简单:

public class DemoUser
{
public string Username { get; set; }
public string Email { get; set; }
public string Language { get; set; }
public Gender Gender { get; set; }
public int[] RoleIds { get; set; }
public List<ScheduledJob> Jobs { get; set; }
} public class ScheduledJob
{
public string Job { get; set; }
public string From { get; set; }
public string To { get; set; }
} public enum Gender
{
Unknown = 0,
Male = 1,
Female = 2
}

请注意SaveUser这个action的参数,一个比较复杂的实体类的对象。DefaultModelBinder会自动将这个复杂的表单解析出来。这个保存的action将参数user直接序列化JSON字符串返回到浏览器。

下面看看HTML和JS。

HTML:

 1 <form id="formUserEditor" action="/demo/saveuser" method="POST">
2 <table class="form">
3 <colgroup>
4 <col width="100"/>
5 <col width="auto"/>
6 </colgroup>
7 <tbody>
8 <tr>
9 <td>用户名:</td>
10 <td>
11 <input id="txtUsername" type="text" name="username" />
12 </td>
13 </tr>
14 <tr>
15 <td>Email:</td>
16 <td>
17 <input id="txtEmail" type="text" name="email" />
18 </td>
19 </tr>
20 <tr>
21 <td>语言:</td>
22 <td>
23 <select id="ddlLanguages" name="language">
24 <option value="zh-cn">中文</option>
25 <option value="en-us">英文</option>
26 </select>
27 </td>
28 </tr>
29 <tr>
30 <td>性别:</td>
31 <td id="genders">
32 <input type="radio" name="gender" value="@(Taoad.Web.Publics.Controllers.Gender.Unknown)" id="rdUnknown" />
33 <label for="rdUnknown">未知</label>
34
35 <input type="radio" name="gender" value="@(Taoad.Web.Publics.Controllers.Gender.Male)" id="rdMale" />
36 <label for="rdMale">男</label>
37
38 <input type="radio" name="gender" value="@(Taoad.Web.Publics.Controllers.Gender.Female)" id="rdFemale" />
39 <label for="rdFemale">女</label>
40 </td>
41 </tr>
42 <tr>
43 <td>角色:</td>
44 <td id="roles">
45 <input type="checkbox" name="roleids" value="1" id="cb1" />
46 <label for="cb1">管理员</label>
47
48 <input type="checkbox" name="roleids" value="2" id="cb2" />
49 <label for="cb2">部门经理</label>
50
51 <input type="checkbox" name="roleids" value="3" id="cb3" />
52 <label for="cb3">客户</label>
53 </td>
54 </tr>
55 <tr>
56 <td>时间:</td>
57 <td>
58 <ul id="jobs">
59
60 </ul>
61 <input type="button" value="添加" id="btnAddJob"/>
62 </td>
63 </tr>
64 <tr>
65 <td></td>
66 <td>
67 <input type="button" value="保存" id="btnSave"/>
68 <input type="button" value="取消" id="btnCancel" />
69 </td>
70 </tr>
71 </tbody>
72 </table>
73 </form>
74 <hr/>
75 <div id="json">
76
77 </div>

JS:

 1 <script language="javascript" type="text/javascript">
2 $(document).ready(function () {
3 $("#btnAddJob").click(function() {
4 var $newLi = $(html);
5 $("#jobs").append($newLi);
6 bindLi($newLi);
7 });
8
9 $("#btnSave").click(function() {
10 var data = $("#formUserEditor").serialize();
11 $("#jobs li").each(function(i) {
12 var prefix = "&jobs[" + i + "]";
13 data += prefix + ".job=" + $(this).find(".job-id").val();
14 data += prefix + ".from=" + $(this).find(".job-from").val();
15 data += prefix + ".to=" + $(this).find(".job-to").val();
16 });
17 demo.ajax.post("/demo/saveuser", data, function(json) {
18 $("#json").html(json);
19 });
20 });
21 });
22
23 function bindLi(li) {
24 $(li).find(".btn-add").click(function () {
25 var $li = $(this).closest("li");
26 var $newLi = $(html);
27 $li.after($newLi);
28 bindLi($newLi);
29 });
30 $(li).find(".btn-delete").click(function () {
31 $(this).closest("li").remove();
32 });
33 }
34
35 var html = '<li>\
36 <select class="job-id">\
37 <option value="job1">工作1</option>\
38 <option value="job2">工作2</option>\
39 </select>\
40 <input type="text" placeholder="开始时间" class="job-from"/>\
41 ————\
42 <input type="text" placeholder="结束时间" class="job-to"/>\
43 <a href="javascript:void(0);" class="btn-add">添加</a> |\
44 <a href="javascript:void(0);" class="btn-delete">删除</a>\
45 </li>';
46 </script>

如果是如下的表单数据:

点击保存之后,返回的JSON数据为:

可以看到所有的表单数据都保存成功了。

再看看request信息:

请注意content-type的值。

实际上,POST到服务器的表单数据只是一个字符串,如下:

复制出来就是下面这样的字符串:

ajax=true&username=leo&email=leo%40gmail.com&language=zh-cn&gender=Unknown&roleids=1&roleids=3&jobs[0].job=job1&jobs[0].from=9:00&jobs[0].to=10:00&jobs[1].job=job2&jobs[1].from=10:00&jobs[1].to=11:00

由此可知,可用JS来拼接字符串,将整个表单通过键值对的形式序列化成一个字符串,再将该字符串传到服务器,这时DefaultModelBinder就可以自动解析实体类了。

关键点在于,对于List或者数组类型的数据,要加上数组下标。这样,任意复杂的数据结构,DefaultModelBinder都可以自动解析了。

思考一:

如果表单数据的键带有”demouser”的前缀,如下所示,那么这个action的参数还能自动解析吗?

Demouser.username=leo&demouser.email=leo@gmail.com&demouser.jobs[0].job=job1&.....

Action如下:

 1 [HttpPost]
2 public string SaveUser(DemoUser user)
3 {
4 var result = string.Empty;
5 if (user != null)
6 {
7 result = Serializer.ToJson(user);
8 }
9 return result;
10 }

如果action参数名称又改成demouser呢?

思考二:

如果将action的参数名称改成如下代码所示,那么是否可以自动解析?(表单数据不带”demouser”前缀)

ajax=true&username=leo&email=leo%40gmail.com&language=zh-cn&gender=Unknown&roleids=1&roleids=3&jobs[0].job=job1&jobs[0].from=9:00&jobs[0].to=10:00&jobs[1].job=job2&jobs[1].from=10:00&jobs[1].to=11:00

Action如下:

 1 [HttpPost]
2 public string SaveUser(DemoUser demoUser)
3 {
4 var result = string.Empty;
5 if (demoUser != null)
6 {
7 result = Serializer.ToJson(demoUser);
8 }
9 return result;
10 }

思考三:

如果action的参数是用另一个类包起来了,如下代码所示,那么表单数据应该是怎么样的字符串才能使DefaultModelBinder可以自动解析?

 1 public class UserEditorViewModel
2 {
3 public DemoUser DemoUser { get; set; }
4 }
5
6 [HttpPost]
7 public string SaveUser(UserEditorViewModel model)
8 {
9 var result = string.Empty;
10 if (model != null)
11 {
12 result = Serializer.ToJson(model);
13 }
14 return result;
15 }

思考四:

如果action是这样定义的,那么表单数据的字符串又该怎么拼?

 1 [HttpPost]
2 public string SaveUser(DemoUser demoUser, List<ScheduledJob> scheduledJobs)
3 {
4 var result = string.Empty;
5 if (demoUser != null)
6 {
7 demoUser.Jobs = scheduledJobs;
8 result = Serializer.ToJson(demoUser);
9 }
10 return result;
11 }

ASP.NET MVC DefaultModelBinder的更多相关文章

  1. 你从未知道如此强大的ASP.NET MVC DefaultModelBinder

    看到很多ASP.NET MVC项目还在从request.querystring或者formContext里面获取数据,这实在是非常落后的做法.也有的项目建了大量的自定义的modelbinder,以为很 ...

  2. asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证

    原文:asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证 在前面的文章中我们曾经涉及到ControllerActionInvoker类GetPara ...

  3. ASP.NET MVC——模型绑定

    这篇文章我们来讲讲模型绑定(Model Binding),其实在初步了解ASP.NET MVC之后,大家可能都会产生一个疑问,为什么URL片段最后会转换为例如int型或者其他类型的参数呢?这里就不得不 ...

  4. ASP.NET MVC Model验证(五)

    ASP.NET MVC Model验证(五) 前言 上篇主要讲解ModelValidatorProvider 和ModelValidator两种类型的自定义实现, 然而在MVC框架中还给我们提供了其它 ...

  5. ASP.NET MVC Model验证(四)

    ASP.NET MVC Model验证(四) 前言 本篇主要讲解ModelValidatorProvider 和ModelValidator两种类型的自定义实现,前者是Model验证提供程序,而Mod ...

  6. ASP.NET MVC Model验证(三)

    ASP.NET MVC Model验证(三) 前言 上篇中说到在MVC框架中默认的Model验证是在哪里验证的,还讲到DefaultModelBinder类型的内部执行的示意图,让大家可以看到默认的M ...

  7. ASP.NET MVC Model验证(二)

    ASP.NET MVC Model验证(二) 前言 上篇内容演示了一个简单的Model验证示例,然后在文中提及到Model验证在MVC框架中默认所处的位置在哪?本篇就是来解决这个问题的,并且会描述一下 ...

  8. ASP.NET MVC Model验证(一)

    ASP.NET MVC Model验证(一) 前言 前面对于Model绑定部分作了大概的介绍,从这章开始就进入Model验证部分了,这个实际上是一个系列的Model的绑定往往都是伴随着验证的.也会在后 ...

  9. ASP.NET MVC Model绑定(一)

    ASP.NET MVC Model绑定(一) 前言 ModelMetadata系列的结束了,从本篇开始就进入Model绑定部分了,这个系列阅读过后你会对Model绑定有个比较清楚的了解, 本篇对于Mo ...

随机推荐

  1. 使用C# DES解密java DES加密的字符串

    转自 microAllen   最近需要使用C#的DES解密工具类解密字符串,但是要解密的字符串是使用java进行DES加密的,去网上查了关于C#和java关于DES加密解密的资料,发现可以相互加密解 ...

  2. [HDU] 1068 Girls and Boys(二分图最大匹配)

    题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1068 本题求二分图最大独立点集.因为最大独立点集=顶点数-最大匹配数.所以转化为求最大匹配.因为没有给 ...

  3. JUnit使用Eclipse建立Test Suite - 就是爱Java

    当JUnit有多个或所有的Test Case要执行,此时就需要Test Suite来管理众多的Test Case,利用Eclipse的整合开发环境,可以针对多个不相关的Test Case或Test S ...

  4. KEIL中的变量相关

    例:sfr P0=0x80表示P0口地址为80H:(sfr是字节操作) sfr16 T2=0xCC表示T2口地址的低地址为TL2=0XCC,高地址为TH2=0XCD(sfr16是字操作) 头文件reg ...

  5. IT人大学生活之“做点正经事”

    最近一直主抓部门的人事招聘工作:很多到手的简历,特别是毕业一年之内的同学的简历上面都会写到:在xxx餐饮公司实习,获得了与人交流的经验:在学生会组织了哪些文体活动:在大四参加了一些与软件开发不相关的一 ...

  6. euctb

  7. day57:00:26:34

    今天开始用博客记录倒计时,也只是为了看看今天做了什么.这也是我第一用博客园记录考研生活了 倒计时57天,我在想每天花时间在这记录生活会不会浪费复习的时间,其实不会的了,不去看微博,少刷新闻....仔细 ...

  8. Windows系统基本概念

    windows API:被文档化的可以调用的子例程,如CreateProcess 原生的系统服务(执行体系统服务):未被文档化的.可以再用户模式下调用的底层服务,如NtCreateProcess 内核 ...

  9. 第33讲 UI组件_进度条ProcessBar和消息队列处理器handler

    第33讲UI组件_进度条ProcessBar和消息队列处理器handler 1. 进度条ProcessBar 一个可视化的进度指示器,代表正在执行的耗时任务.可以为用户展示一个进度条,表示正在执行的任 ...

  10. c3p0数据源定义

    <!-- c3p0 connection pool configuration --> <bean id="testDataSource" class=" ...