Nancy之ModelBinding(模型绑定)
过年前的最后一篇博客,决定留给Nancy中的ModelBinding
还是同样的,我们与MVC结合起来,方便理解和对照
先来看看MVC中简单的ModelBinding吧
// POST: Authors/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "AuthorId,AuthorName,AuthorGender,AuthorEmail,AuthorAddress,AuthorPhone")] Author author)
{
if (ModelState.IsValid)
{
db.Authors.Add(author);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(author);
}
上面的代码是我用下面类型的控制器生成的一个添加方法,里面就用到了ModelBinding

像这样比较简单的模型绑定,大家应该是很熟悉了吧!
或许已经烂熟于心了。
MVC中关于Model Binding的详细解读可以参见下面的,真的超详细,我就不再展开了
[ASP.NET MVC 小牛之路]15 - Model Binding
ModelBinder——ASP.NET MVC Model绑定的核心
下面就来看看Nancy中的model binding吧。
先来看个具体的例子,我们顺着这个例子来讲解这一块的内容
这个例子我们要用到的引用有Nancy,Nancy.Hosting.Aspnet
我们先来看看它默认的绑定
先建立一个模型Employee
public class Employee
{
public Employee()
{
this.EmployeeNumber = "Num1";
this.EmployeeName = "Catcher8";
this.EmployeeAge = ;
}
public string EmployeeNumber { get; set; }
public string EmployeeName { get; set; }
public int EmployeeAge { get; set; }
public List<string> EmployeeHobby { get; set; }
}
我们在这个模型中,给部分字段设置了默认值。
建立一个视图default.html,用于测试Nancy中默认的ModelBinding
<!DOCTYPE html>
<html>
<head>
<title>default</title>
<meta charset="utf-8" />
</head>
<body>
<form action="/default" method="post">
<label>员工编号</label>
<input type="text" name="EmployeeNumber" /> <br />
<label>员工姓名</label>
<input type="text" name="EmployeeName" /> <br />
<label>员工年龄</label>
<input type="text" name="EmployeeAge" /> <br /> <input type="checkbox" name="EmployeeHobby" value="篮球" />篮球
<input type="checkbox" name="EmployeeHobby" value="足球" />足球
<input type="checkbox" name="EmployeeHobby" value="排球" />排球
<input type="checkbox" name="EmployeeHobby" value="网球" />网球
<br />
<input type="submit" value="提交" />
</form>
</body>
</html>
然后我们建立一个TestModule.cs,在里面演示了各种不同方式下的binding
public class TestModule : NancyModule
{
public TestModule()
{
Get["/default"] = _ =>
{
return View["default"];
};
Post["/default"] = _ =>
{
Employee employee_Empty = new Employee();
//这种写法有问题,应该是 Employee xxx = this.Bind(); 才对!
//因为这里的this.Bind() 是 dynamic 类型,没有直接指明类型
//所以它会提示我们 “找不到此对象的进一步信息”
var employee_Using_Bind = this.Bind(); //这里在bind的时候指明了类型。这个会正常绑定数据。(推荐这种写法)
var employee_Using_BindWithTModel = this.Bind<Employee>();
//这里是将数据绑定到我们实例化的那个employee_Empty对象
//运行到这里之后,会发现employee_Empty的默认值被替换了!!
var employee_Using_BindTo = this.BindTo(employee_Empty);
//与上面的写法等价!
var employee_Using_BindToWithTModel = this.BindTo<Employee>(employee_Empty);
//这个主要是演示“黑名单”的用法,就是绑定数据的时候忽略某几个东西
//这里忽略了EmployeeName和EmployeeAge,所以得到的最终还是我们设置的默认值
var employee_Using_BindAndBlacklistStyle1 = this.Bind<Employee>(e=>e.EmployeeName,e=>e.EmployeeAge);
//与上面的写法等价,演示不同的写法而已!
var employee_Using_BindAndBlacklistStyle2 = this.Bind<Employee>("EmployeeName", "EmployeeAge");
return Response.AsRedirect("/default");
};
}
}
下面来看看运行的结果

我们在表单填下了这些内容,现在我们监视上面的各个值的变化

这几个最终都是一样的效果!!这里说最终,是因为我们的employee_Empty刚实例化时,应该是我们设置的默认值。
employee_Using_BindAndBlacklistStyle1和employee_Using_BindAndBlacklistStyle2是在Bind后面带了参数的,
这些参数就是所谓的黑名单,就是绑定的时候忽略掉。然后结果就是我们设置的默认值Catcher8和18。
至于它为什么这样就能绑定上,我们看了自定义的绑定之后可能会清晰不少。
接下来就是使用自定义的绑定方法:
模型我们还是用刚才的Employee.cs
此处新添加一个视图custom.html,基本和前面的default.html一致,换了个action
<!DOCTYPE html>
<html>
<head>
<title>custom</title>
<meta charset="utf-8" />
</head>
<body>
<form action="/custom" method="post">
<label>员工编号</label>
<input type="text" name="EmployeeNumber" /> <br />
<label>员工姓名</label>
<input type="text" name="EmployeeName" /> <br />
<label>员工年龄</label>
<input type="text" name="EmployeeAge" /> <br />
<input type="checkbox" name="EmployeeHobby" value="篮球" />篮球
<input type="checkbox" name="EmployeeHobby" value="足球" />足球
<input type="checkbox" name="EmployeeHobby" value="排球" />排球
<input type="checkbox" name="EmployeeHobby" value="网球" />网球
<br />
<input type="submit" value="提交" />
</form>
</body>
</html>
至关重要的一步!!!编写我们的ModelBinder,这个ModelBinder要实现IModelBinder这个接口!
public class MyModelBinder : IModelBinder
{
public bool CanBind(Type modelType)
{
return modelType == typeof(Employee);
}
public object Bind(NancyContext context, Type modelType, object instance, BindingConfig configuration, params string[] blackList)
{
var employee = (instance as Employee) ?? new Employee();
employee.EmployeeName = context.Request.Form["EmployeeName"] ?? employee.EmployeeName;
employee.EmployeeNumber = context.Request.Form["EmployeeNumber"] ?? employee.EmployeeNumber;
employee.EmployeeAge = 24;//我们把年龄写死,方便看见差异
employee.EmployeeHobby = ConvertStringToList(context.Request.Form["EmployeeHobby"]) ?? employee.EmployeeHobby;
return employee;
} private List<string> ConvertStringToList(string input)
{
if (string.IsNullOrEmpty(input))
{
return null;
}
var items = input.Split(',');
return items.AsEnumerable().ToList<string>();
}
}
然后在我们的TestModule.cs中添加如下代码
Get["/custom"] = _ =>
{
return View["custom"];
};
Post["/custom"] = x =>
{
//此时就会调用我们自己定义的Binder了
var employee1 = this.Bind<Employee>();
Employee employee2 = this.Bind();
return Response.AsRedirect("/custom");
};
下面看看运行效果

我们还是在表单输入这些内容,同时对employee1和employee2添加监视

清楚的看到,我们自定义的binder生效了,年龄就是我们设定的24!
Nancy中,还有比较方便的是json和xml也同样能绑定。由于这两个很相似,所以这里就只介绍json。
同样的,例子说话!
添加一个json.html视图
<!DOCTYPE html>
<html>
<head>
<title>default</title>
<meta charset="utf-8" /> <script src="../../content/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
var dat = "{\"EmployeeName\":\"catcher1234\", \"EmployeeAge\":\"33\"}";
$.ajax({
type: "POST",
url: "/json",
contentType: "application/json",
data: dat,
success: function (data) {
alert("Response:\n" + data);
}
});
});
</script>
</head>
<body>
</body>
</html>
在这里,偷懒了(节省点时间),我是直接写死了两个值,然后打印出这个employee的相关属性。
还有一点要注意的是。引用的js文件,不想写convention配置就把js放到content文件夹,具体的可参见我前面的bolg Nancy之静态文件处理。
Get["/json"] = _ =>
{
return View["json"];
};
Post["/json"] = _ =>
{
var employee = this.Bind<Employee>();
var sb = new StringBuilder();
sb.AppendLine("绑定的employee的值:");
sb.Append("编号: ");
sb.AppendLine(employee.EmployeeNumber);
sb.Append("姓名: ");
sb.AppendLine(employee.EmployeeName);
sb.Append("年龄: ");
sb.AppendLine(employee.EmployeeAge.ToString());
return sb.ToString();
};
运行看看效果

再来看看我们监视的情况!!

很nice,正是我们想要的结果,编号没有赋值,自动取了默认值!
跟往常一样,简单分析一下这一块的源码。
ModelBinding在Nancy这个项目下面,里面的内容如下:

很明显,我们应该先看看DefaultBinder.cs,因为所有的默认实现,Nancy都会带Default的字样
DefaultBinder实现了IBinder这个接口,这个接口里面就一个东西Bind
/// <summary>
/// Binds incoming request data to a model type
/// </summary>
public interface IBinder
{
/// <summary>
/// Bind to the given model type
/// </summary>
/// <param name="context">Current context</param>
/// <param name="modelType">Model type to bind to</param>
/// <param name="configuration">The <see cref="BindingConfig"/> that should be applied during binding.</param>
/// <param name="blackList">Blacklisted property names</param>
/// <param name="instance">Existing instance of the object</param>
/// <returns>Bound model</returns>
object Bind(NancyContext context, Type modelType, object instance, BindingConfig configuration, params string[] blackList);
}
这就是我们ModelBinding的关键所在!
DefaultBinder里面的实现就是
先判断绑定的类型是不是数组集合,是的话,一种处理策略,不是的话,另一种处理策略,
在里面的判断中还有一个重要的概念是Binding Configuration。因为这个Configuration可以修改我们绑定的行为
这里我直接截了官网文档的图来展示

BodyOnly设置为true的时候,一旦主体被绑定,binder就会立刻停止。
IgnoreErrors为false时,就不会在继续进行绑定,为true时就会继续绑定,默认值是false。
Overwrite为ture时,允许binder去覆盖我们设置的那些默认值,为false时,就是不允许,默认值是true!
DefaultBinder里面有个GetDataFields的私有方法
private IDictionary<string, string> GetDataFields(NancyContext context)
{
var dictionaries = new IDictionary<string, string>[]
{
ConvertDynamicDictionary(context.Request.Form),
ConvertDynamicDictionary(context.Request.Query),
ConvertDynamicDictionary(context.Parameters)
};
return dictionaries.Merge();
}
从中我们可以看出,它处理绑定的时候用到了字典,包含了表单的数据、url的参数,这点与mvc里面的基本一致!
所以我们在写页面的时候,我们只要把表单元素的name属性设置为对应的字段名就可以,同样的,这个在mvc中也一致
下面看看ITypeConverter这个接口
public interface ITypeConverter
{
bool CanConvertTo(Type destinationType, BindingContext context); object Convert(string input, Type destinationType, BindingContext context);
}
这个接口提供了一种转换类型的方法
CanConvertTo 是判断是否能转化为目的类型,
Nancy默认的Converters包含了Collection、DateTime、Fallback和Numeric
当然,有了这个接口,我们可以实现更多的拓展,怎么用着方便怎么来!
当然不能忘了我们自定义模型绑定用到的接口 IModelBinder
public interface IModelBinder : IBinder
{
bool CanBind(Type modelType);
}
IModerBinder这个接口除了自己定义的CanBind方法外,还继承了IBinder这个接口,所以我们自定义ModelBinder的时候只需要
实现这个接口就可以了。作用就是绑定数据到相应的模型中。
最后就讲讲“黑名单”的内容!
“黑名单”的实现,还用到了DynamicModelBinderAdapter这个东西,但最为主要的是
DefaultBinder里面的CreateBindingContext这个私有方法!
private BindingContext CreateBindingContext(NancyContext context, Type modelType, object instance, BindingConfig configuration, IEnumerable<string> blackList, Type genericType)
{
return new BindingContext
{
Configuration = configuration,
Context = context,
DestinationType = modelType,
Model = CreateModel(modelType, genericType, instance),
ValidModelBindingMembers = GetBindingMembers(modelType, genericType, blackList).ToList(),
RequestData = this.GetDataFields(context),
GenericType = genericType,
TypeConverters = this.typeConverters.Concat(this.defaults.DefaultTypeConverters),
};
}
从中我们可以看到GetBindingMembers用到了blackList,再看看这个方法
private static IEnumerable<BindingMemberInfo> GetBindingMembers(Type modelType, Type genericType, IEnumerable<string> blackList)
{
var blackListHash = new HashSet<string>(blackList, StringComparer.Ordinal); return BindingMemberInfo.Collect(genericType ?? modelType)
.Where(member => !blackListHash.Contains(member.Name));
}
看到这个,行了,基本就懂了!
member => !blackListHash.Contains(member.Name)
这个表达式就是起到了真正的过滤作用啦!!!
ModelBinding就讲到这里了。
最后献上本次的代码示例:
https://github.com/hwqdt/Demos/tree/master/src/NancyDemoForModelBinding
Nancy之ModelBinding(模型绑定)的更多相关文章
- NancyFX 第七章 模型绑定和验证
任何优秀的框架,都能传递参数.在之前的路由章节,我们已经看到了如何在URL中传递参数. 能够传递简单的参数当然好,特别是在设计那些从数据库读取记录的API设计中.但是,很多情况下也是需要传递复杂对象. ...
- NancyFx 2.0的开源框架的使用-ModelBinding(实现绑定)
NancyFx框架中使用绑定模型 新建一个空的Web程序 然后安装Nuget库里面的包 Nancy Nancy.Hosting.Aspnet Nancy.ViewEnglines.Spark 并在We ...
- ASP.NET Core MVC 模型绑定用法及原理
前言 查询了一下关于 MVC 中的模型绑定,大部分都是关于如何使用的,以及模型绑定过程中的一些用法和概念,很少有关于模型绑定的内部机制实现的文章,本文就来讲解一下在 ASP.NET Core MVC ...
- ASP.NET没有魔法——ASP.NET MVC 模型绑定
在My Blog中已经有了文章管理功能,可以发布和修改文章,但是对于文章内容来说,这里缺少最重要的排版功能,如果没有排版的博客很大程度上是无法阅读的,由于文章是通过浏览器查看的,所以文章的排版其实与网 ...
- ASP.NET Core 2.2 十九. Action参数的映射与模型绑定
前文说道了Action的激活,这里有个关键的操作就是Action参数的映射与模型绑定,这里即涉及到简单的string.int等类型,也包含Json等复杂类型,本文详细分享一下这一过程.(ASP.NET ...
- 运用模型绑定和web窗体显示和检索数据(Retrieving and displaying data with model binding and web forms)
原文 http://www.asp.net/web-forms/overview/presenting-and-managing-data/model-binding/retrieving-data ...
- TryUpdateModel方法 模型绑定
文档资料:https://msdn.microsoft.com/zh-cn/library/ee728634.aspx 有很多重载其中 Controller.TryUpdateModel<TMo ...
- 【ASP.NET Core】MVC 控制器的模型绑定(宏观篇)
欢迎来到老周的水文演播中心. 咱们都知道,MVC的控制器也可以用来实现 Web API 的(它们原本就是一个玩意儿),区别嘛也就是一个有 View 而另一个没有 View.于是,在依赖注入的服务容器中 ...
- 【ASP.NET Core】MVC模型绑定——实现同一个API方法兼容JSON和Form-data输入
在上一篇文章中,老周给大伙伴们大致说了下 MVC 下的模型绑定,今天咱们进行一下细化,先聊聊模型绑定中涉及到的一些组件对象. ------------------------------------- ...
随机推荐
- 新书到手 TRANSACTION PROCESSING:CONCEPTS AND TECHNIQUES
新书到手 TRANSACTION PROCESSING:CONCEPTS AND TECHNIQUES Jim Gray大神的著作 本文版权归作者所有,未经作者同意不得转载.
- c# 元组Tuple
Tuple类型像一个口袋,在出门前可以把所需的任何东西一股脑地放在里面.您可以将钥匙.驾驶证.便笺簿和钢笔放在口袋里,您的口袋是存放各种东西的收集箱.C# 4.0引入的一个新特性 Tuple类型与口袋 ...
- Visual Studio 2013 Web开发
cnbeta新闻:微软正式发布Visual Studio 2013 RTM版,微软还发布了Visual Studio 2013的最终版本..NET 4.5.1以及Team Foundation Ser ...
- Windows Azure一些小技巧集合
我最近做了一个Windows Azure上面的项目,自己在做的过程中遇到了很多问题.有的是我自己摸索解决,有的是到网上寻找零碎的信息结合起来解决的.我感觉应当把某些解决方法集中一下,方便我以后查阅,也 ...
- Python黑帽编程 3.5 DTP攻击
Python黑帽编程 3.5 DTP攻击 在上一节,<Python黑帽编程 3.4 跨越VLAN>中,我们讨论了一般的VLAN中实施攻击的方法,这一节属于扩展内容,简单演示下Cisco特有 ...
- ASP.NET Web API的Controller是如何被创建的?
Web API调用请求的目标是定义在某个HttpController类型中的某个Action方法,所以消息处理管道最终需要激活目标HttpController对象.调用请求的URI会携带目标HttpC ...
- LeetCode Note 1st,practice makes perfect
1. Two Sum Given an array of integers, return indices of the two numbers such that they add up to a ...
- 2013 duilib入门简明教程 -- 界面布局(9)
上一个教程实现的标题栏代码中,并没有看到处理自适应窗口大小的代码,但是窗口大小变化后,按钮的位置会跟着变化,这是因为我们将按钮放到了HorizontalLayout.VerticalLayou ...
- css绝对定位如何在不同分辨率下的电脑正常显示定位位置?
有时候我们在写页面中,会发现绝对定位的父级元素已经相对定位了,但是在不同分辨率的电脑下,绝对定位还是会错乱,似乎父级的相对定位并没有起了作用. 首先要明白如下几个原理: 1.笔记本电脑的分辨率一般为1 ...
- SQL Server 自动增长过大
一.背景 我们遇到的问题如下图所示:自动增长无端端就按照这样的比例进行增长: (Figure1:问题所在) 尝试使用SSMS修改自动增长值,就会出现下面的错误: (Figure2:错误信息) 遇到上面 ...