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


    看到很多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 }

    欢迎讨论。

  • 相关阅读:
    130517Dev GridControl建立多行复杂表头(Banded View)时,统计列与对应列无法对齐的解决办法
    C&C++标准库
    Linux操作系统下的多线程编程详细解析
    Ubuntu12.04用户以root身份登录
    ubuntu永久修改主机名
    linux信号 linux signal
    淘宝api 登录验证
    淘宝开店 防骗 易赛加款诈骗|冲q币恶意差评
    面试..
    test
  • 原文地址:https://www.cnblogs.com/leotsai/p/ASPNET-MVC-DefaultModelBinder.html
Copyright © 2020-2023  润新知