你从未知道如此强大的ASP.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 = ,
Male = ,
Female =
}
请注意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;
}
欢迎讨论。
你从未知道如此强大的ASP.NET MVC DefaultModelBinder的更多相关文章
- ASP.NET MVC DefaultModelBinder
转载自 Leo‘s Blog 看到很多ASP.NET MVC项目还在从request.querystring或者formContext里面获取数据,这实在是非常落后的做法.也有的项目建了大量的自定义的 ...
- ASP.NET MVC分页组件MvcPager 2.0版发布暨网站全新改版
MvcPager分页控件是在ASP.NET MVC Web应用程序中实现分页功能的一系列扩展方法,该分页控件的最初的实现方法借鉴了网上流行的部分源代码, 尤其是ScottGu的PagedList< ...
- MvcPager 概述 MvcPager 分页示例 — 标准Ajax分页 对SEO进行优化的ajax分页 (支持asp.net mvc)
该示例演示如何使用MvcPager最基本的Ajax分页模式. 使用AjaxHelper的Pager扩展方法来实现Ajax分页,使用Ajax分页模式时,必须至少指定MvcAjaxOptions的Upda ...
- ASP.NET MVC学习之视图篇(1)
一.前言 不知道还有多少读者从第一篇开始一直学习到如今,笔者也会一直坚持将ASP.NET MVC的学习完美的结束掉,然后开始写如何配合其他框架使用ASP.NET MVC的随笔.当然笔者后面的随笔如果没 ...
- 应用程序框架实战三十三:表现层及ASP.NET MVC介绍(二)
最近的更新速度越来越慢,主要是项目上比较忙,封装EasyUi也要花很多时间.不过大家请放心,本系列不会半途夭折,并且代码干货也会持续更新.本文继续介绍表现层和Asp.net Mvc,我将在本篇讨论一些 ...
- ASP.NET MVC 应用提速的十种方法
[编者按]本文作者为 DZone 社区的最具价值博主(MVB) Jonathan Danylko,主要介绍为 ASP.NET MVC 应用提速的十种方法.由国内 ITOM 管理平台 OneAPM 编译 ...
- asp.net mvc vs web form
译者介绍 小小.NET学童,滴答…滴答…的雨…… 正文如下======================================================= 原文示例(VS2012): 1 ...
- 表现层及ASP.NET MVC介绍(二)
表现层及ASP.NET MVC介绍(二) 最近的更新速度越来越慢,主要是项目上比较忙,封装EasyUi也要花很多时间.不过大家请放心,本系列不会半途夭折,并且代码干货也会持续更新.本文继续介绍表现层和 ...
- 为什么说JAVA中要慎重使用继承 C# 语言历史版本特性(C# 1.0到C# 8.0汇总) SQL Server事务 事务日志 SQL Server 锁详解 软件架构之 23种设计模式 Oracle与Sqlserver:Order by NULL值介绍 asp.net MVC漏油配置总结
为什么说JAVA中要慎重使用继承 这篇文章的主题并非鼓励不使用继承,而是仅从使用继承带来的问题出发,讨论继承机制不太好的地方,从而在使用时慎重选择,避开可能遇到的坑. JAVA中使用到继承就会有两 ...
随机推荐
- Base64编码
Base64编码 写在前面 今天在做一个Android app时遇到了一个问题:Android端采用ASE对称加密的数据在JavaWeb(jre1.8.0_7)后台解密时,居然解密失败了!经过测试后发 ...
- windows环境下sublime的nodejs插件详细安装图解
前面的话 搜索了好多文档后,才成功地安装了sublime text3的nodejs插件.为了存档,也为了方便有同样需求的朋友,将其安装过程详细记录如下 安装nodejs 虽然nodejs官网提供了 ...
- C#多线程之线程同步篇1
在多线程(线程同步)中,我们将学习多线程中操作共享资源的技术,学习到的知识点如下所示: 执行基本的原子操作 使用Mutex构造 使用SemaphoreSlim构造 使用AutoResetEvent构造 ...
- 对Thoughtworks的有趣笔试题实践
记得2014年在网上看到Thoughtworks的一道笔试题,当时觉得挺有意思,但是没动手去写.这几天又在网上看到了,于是我抽了一点时间写了下,我把程序运行的结果跟网上的答案对了一下,应该是对的,但是 ...
- golang struct扩展函数参数命名警告
今天在使用VSCode编写golang代码时,定义一个struct,扩展几个方法,如下: package storage import ( "fmt" "github.c ...
- JAVA的内存模型(变量的同步)
一个线程中变量的修改可能不会立即对其他线程可见,事实上也许永远不可见. 在代码一中,如果一个线程调用了MyClass.loop(),将来的某个时间点,另一个线程调用了MyClass.setValue( ...
- Mysql - 性能优化之子查询
记得在做项目的时候, 听到过一句话, 尽量不要使用子查询, 那么这一篇就来看一下, 这句话是否是正确的. 那在这之前, 需要介绍一些概念性东西和mysql对语句的大致处理. 当Mysql Server ...
- Idea下用SBT搭建Spark Helloworld
没用过IDEA工具,听说跟Eclipse差不多,sbt在Idea其实就等于maven在Eclipse.Spark运行在JVM中,所以要在Idea下运行spark,就先要安装JDK 1.8+ 然后加入S ...
- Linux-Rhel6 恢复误删文件
需要工具extundelete(可通过xshell上传下载到linux)知识点:ifconifig eth0 查看ipfdisk /dev/sda 管理磁盘m ...
- 【腾讯Bugly干货分享】微信热补丁Tinker的实践演进之路
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57ad7a70eaed47bb2699e68e Dev Club 是一个交流移动 ...