转载自

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. Kafka笔记--监控系统KafkaOffsetMonitor

    KafkaOffsetMonitor下载链接: http://download.csdn.net/detail/changong28/7930337github官方:https://github.co ...

  2. Hadoop-2.7.1集群环境搭建

    摘自:http://blog.csdn.net/u014039577/article/details/49813531 由于日志数据量越来越大,数据处理的逻辑越来越复杂,同时还涉及到大量日志需要批处理 ...

  3. jquery easyui二次开发总结(二)

    1.easyui tab增加“关闭所有页”.“关闭非当前页”功能. //tab增加“关闭所有页”和“关闭非当前页”的功能 $("#tabs").tabs({ onAdd:funct ...

  4. 关于body/documentElement ---->clientHeight, offsetHeight, scrollHeight

    http://blog.csdn.net/woxueliuyun/article/details/8638427 http://blog.sina.com.cn/s/blog_9dd702d50101 ...

  5. hdu 七夕节

    #include <cstdio> #include <cstring> #include <algorithm> #define maxn 500000 usin ...

  6. FJ省队集训DAY4 T2

    XXX #include<cstdio> #include<iostream> #include<cmath> #include<cstring> #i ...

  7. 管理Undo数据

    SQL> select sum(bytes),status from dba_undo_extents group by status; SUM(BYTES) STATUS ---------- ...

  8. fstream读写UNICODE文件

    今天遇到要处理UNICODE文件的情况,网上找了一圈都是读取出字节,再转的,这个不方便啊!想起了有codecvt这么个东西,顺藤摸瓜,找到了方法. locale utf16(locale(" ...

  9. html p标签换行问题

    /*p标签自动换行*/ p{ word-wrap:break-word; word-break:normal; } /*p强制不换行*/ p{ white-space:nowrap; } /*块级元素 ...

  10. 好多邮箱的SMTP设置

    http://731771490.diandian.com/post/2011-04-20/19576550