首页 > 代码库 > ASP.NET MVC DefaultModelBinder

ASP.NET MVC DefaultModelBinder

转载自

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="http://www.mamicode.com/zh-cn">中文</option>25                         <option value="http://www.mamicode.com/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="http://www.mamicode.com/@(Taoad.Web.Publics.Controllers.Gender.Unknown)" id="rdUnknown" />33                     <label for="rdUnknown">未知</label>34                     35                     <input type="radio" name="gender" value="http://www.mamicode.com/@(Taoad.Web.Publics.Controllers.Gender.Male)" id="rdMale" />36                     <label for="rdMale">男</label>37                     38                     <input type="radio" name="gender" value="http://www.mamicode.com/@(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="http://www.mamicode.com/1" id="cb1" />46                     <label for="cb1">管理员</label>47                     48                     <input type="checkbox" name="roleids" value="http://www.mamicode.com/2" id="cb2" />49                     <label for="cb2">部门经理</label>50                     51                     <input type="checkbox" name="roleids" value="http://www.mamicode.com/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="http://www.mamicode.com/添加" id="btnAddJob"/>62                 </td>63             </tr>64             <tr>65                 <td></td>66                 <td>67                     <input type="button" value="http://www.mamicode.com/保存" id="btnSave"/>68                     <input type="button" value="http://www.mamicode.com/取消" 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 = http://www.mamicode.com/$("#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="http://www.mamicode.com/job1">工作1</option>38                         <option value="http://www.mamicode.com/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