转载自

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. While reading XXX pngcrush caught libpng error: N

    错误一:   While reading /XXX/XXX/XXX/img1.png pngcrush caught libpng error:   Not a PNG filCould not fi ...

  2. IOS NSURL基本操作-备

    NSURL其实就是我们在浏览器上看到的网站地址,这不就是一个字符串么,为什么还要在写一个NSURL呢,主要是因为网站地址的字符串都比较复杂,包括很多请求参数,这样在请求过程中需要解析出来每个部门,所以 ...

  3. 面试题 41 和为s的两个数字VS 和为S的连续整数序列

    (1)和为S的两个数字 bool findNumberWithSum(int data[], int length, int sum, int &numb1, int &numb2){ ...

  4. Visual Studio中的lib的链接顺序

    描述:如果有一个exe工程,它依赖于A.lib,B.lib,A.lib和B.DLL我同样有他们的源码工程.依赖顺序是这样的exe->A.lib->B.DLL.那么如果我改动了B的源码,编译 ...

  5. codility上的问题 (19)Sigma 2012

    题目: 像最大直方图一样给定一个数组是每个单位长度上的高度,求至少几个矩形可以拼出这个形状. 例如:给出的数组 H[0] = 8 H[1] = 8 H[2] = 5 H[3] = 7 H[4] = 9 ...

  6. Java中一些常用的代码

    总结一下最近程序中用到的代码吧,大部分不是自己写的,放到这里,备份一下,以后忘记了来看一下: //正则表达式验证手机号 public static void phoneTest(String phon ...

  7. zoj2112

    题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2112 经典的动态区间第K大. 用树状数组套线段树. 对原数组建一个树 ...

  8. HDU 3586 : Information Disturbing

    Problem Description In the battlefield , an effective way to defeat enemies is to break their commun ...

  9. apache2 httpd 基于域名的虚拟主机配置 for centos6X 和debian-8

    全系统虚拟主机: for debian 系统的apache2 域名 虚拟主机

  10. git术语解释staging,index,cache

    当我在使用git的时候,有三个东西的出现,一度让我非常困扰,就如题所述,staging,index,和cache. 比如,当我阅读git官网提供的电子书<Pro Git>的时候,最初一章里 ...