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

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

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

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

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

public class DemoController : PublicControllerBase
{
    public ActionResult UserEditor()
    {
        return View();
    }

    [HttpPost]
    public string SaveUser(DemoUser user)
    {
        var result = string.Empty;
        if (user != null)
        {
            result = Serializer.ToJson(user);
        }
        return result;
    }
}

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

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:

<form id="formUserEditor" action="/demo/saveuser" method="POST">
    <table class="form">
        <colgroup>
            <col width="100"/>
            <col width="auto"/>
        </colgroup>
        <tbody>
            <tr>
                <td>用户名:</td>
                <td>
                    <input id="txtUsername" type="text" name="username" />
                </td>
            </tr>
            <tr>
                <td>Email:</td>
                <td>
                    <input id="txtEmail" type="text" name="email" />
                </td>
            </tr>
            <tr>
                <td>语言:</td>
                <td>
                    <select id="ddlLanguages" name="language">
                        <option value="zh-cn">中文</option>
                        <option value="en-us">英文</option>
                    </select>
                </td>
            </tr>
            <tr>
                <td>性别:</td>
                <td id="genders">
                    <input type="radio" name="gender" value="@(Taoad.Web.Publics.Controllers.Gender.Unknown)" id="rdUnknown" />
                    <label for="rdUnknown">未知</label>

                    <input type="radio" name="gender" value="@(Taoad.Web.Publics.Controllers.Gender.Male)" id="rdMale" />
                    <label for="rdMale"></label>

                    <input type="radio" name="gender" value="@(Taoad.Web.Publics.Controllers.Gender.Female)" id="rdFemale" />
                    <label for="rdFemale"></label>
                </td>
            </tr>
            <tr>
                <td>角色:</td>
                <td id="roles">
                    <input type="checkbox" name="roleids" value="1" id="cb1" />
                    <label for="cb1">管理员</label>

                    <input type="checkbox" name="roleids" value="2" id="cb2" />
                    <label for="cb2">部门经理</label>

                    <input type="checkbox" name="roleids" value="3" id="cb3" />
                    <label for="cb3">客户</label>
                </td>
            </tr>
            <tr>
                <td>时间:</td>
                <td>
                    <ul id="jobs">

                    </ul>
                    <input type="button" value="添加" id="btnAddJob"/>
                </td>
            </tr>
            <tr>
                <td></td>
                <td>
                    <input type="button" value="保存" id="btnSave"/>
                    <input type="button" value="取消" id="btnCancel" />
                </td>
            </tr>
        </tbody>
    </table>
</form>
<hr/>
<div id="json">

</div>

JS:

<script language="javascript" type="text/javascript">
    $(document).ready(function () {
        $("#btnAddJob").click(function() {
            var $newLi = $(html);
            $("#jobs").append($newLi);
            bindLi($newLi);
        });

        $("#btnSave").click(function() {
            var data = $("#formUserEditor").serialize();
            $("#jobs li").each(function(i) {
                var prefix = "&jobs[" + i + "]";
                data += prefix + ".job=" + $(this).find(".job-id").val();
                data += prefix + ".from=" + $(this).find(".job-from").val();
                data += prefix + ".to=" + $(this).find(".job-to").val();
            });
            demo.ajax.post("/demo/saveuser", data, function(json) {
                $("#json").html(json);
            });
        });
    });

    function bindLi(li) {
        $(li).find(".btn-add").click(function () {
            var $li = $(this).closest("li");
            var $newLi = $(html);
            $li.after($newLi);
            bindLi($newLi);
        });
        $(li).find(".btn-delete").click(function () {
            $(this).closest("li").remove();
        });
    }

    var html = '<li>\
                    <select class="job-id">\
                        <option value="job1">工作1</option>\
                        <option value="job2">工作2</option>\
                    </select>\
                    <input type="text" placeholder="开始时间"  class="job-from"/>\
                    ————\
                    <input type="text" placeholder="结束时间"  class="job-to"/>\
                    <a href="javascript:void(0);" class="btn-add">添加</a> |\
                    <a href="javascript:void(0);" class="btn-delete">删除</a>\
                </li>';
</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如下:

[HttpPost]
public string SaveUser(DemoUser user)
{
    var result = string.Empty;
    if (user != null)
    {
        result = Serializer.ToJson(user);
    }
    return result;
}

如果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如下:

[HttpPost]
public string SaveUser(DemoUser demoUser)
{
    var result = string.Empty;
    if (demoUser != null)
    {
        result = Serializer.ToJson(demoUser);
    }
    return result;
}

思考三:

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

public class UserEditorViewModel
{
    public DemoUser DemoUser { get; set; }
}

[HttpPost]
public string SaveUser(UserEditorViewModel model)
{
    var result = string.Empty;
    if (model != null)
    {
        result = Serializer.ToJson(model);
    }
    return result;
}

思考四:

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

[HttpPost]
public string SaveUser(DemoUser demoUser, List<ScheduledJob> scheduledJobs)
{
    var result = string.Empty;
    if (demoUser != null)
    {
        demoUser.Jobs = scheduledJobs;
        result = Serializer.ToJson(demoUser);
    }
    return result;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值