[Route("[controller]")] public class ModelBindController : Controller { [HttpGet] public IActionResult Post(List<Dictionary<string, int>> pairs) { return Ok(); } }
是不是说明.NET Core就不支持了呢?显然不是,我们将参数名称需要修改一致才行,我们将URL上的参数名称修改为和控制器方法上的参数一致(当然类型也要一致,否则也会映射不上),如下:
好了,见识到.NET Core中模型绑定系统的强大,接下来我们快马加鞭去看看模型绑定原理是怎样的吧,GO。
模型绑定原理
了解模型绑定原理有什么作用呢?当.NET Core提供给我们的模型绑定系统不满足我们的需求时,我们可以自定义模型绑定来实现我们的需求,这里我简单说下整个过程是这样的,然后呢,给出我画的一张详细图关于模型绑定的整个过程是这样。当我们在startup中使用services.AddMvc()方法时,说明我们会使用MVC框架,此时在背后对于模型绑定做了什么呢?
【1】初始化ModelBinderProviders集合,并向此集合中添加16个已经实现的ModelBinderProvider
【2】初始化ValuesProviderFactories集合,并向此集合中添加4个ValueFactory
【3】以单例形式注入<IModelBinderFactory,ModelBinderFactory>
【4】添加其他模型元数据信息
接下来到底是怎样将参数进行绑定的呢?首先我们来定义一个IModelBinder接口,如下:
public interface IModelBinder { Task BindModelAsync(ModelBindingContext bindingContext); }
那这个接口用来干嘛呢,通过该接口中定义的方法名称我们就知道,这就是最终我们得到的ModelBinder,继而通过绑定上下文来绑定参数, 那么具体ModelBinder又怎么来呢?接下来定义IModelBinderProvder接口,如下:
public interface IModelBinderProvider { IModelBinder GetBinder(ModelBinderProviderContext context); }
通过IModelBinderProvider接口中的ModelBinderProvderContext获取具体的ModelBinder,那么通过该接口中的方法GetBinder,我们如何获取具体的ModelBinder呢,换而言之,我们怎么去创建具体的ModelBinder呢,在添加MVC框架时我们注入了ModelBinderFactory,此时ModelBinderFactory上场了,代码如下:
public class ModelBinderFactory : IModelBinderFactory { public IModelBinder CreateBinder(ModelBinderFactoryContext context) { ..... } }
那这个方法内部是如何实现的呢?其实很简单,也是在我们添加MVC框架时,初始了16个具体ModelBinderProvider即List<IModelBinderProvider>,此时在这个方法里面去遍历这个集合,此时上述方法内部实现变成如下伪代码:
public class ModelBinderFactory : IModelBinderFactory { public IModelBinder CreateBinder(ModelBinderFactoryContext context) { IModelBinderProvider[] _providers; IModelBinder result = null; for (var i = 0; i < _providers.Length; i++) { var provider = _providers[i]; result = provider.GetBinder(providerContext); if (result != null) { break; } } } }
至于它如何得到是哪一个具体的ModelBinderProvider的,这就涉及到具体细节实现了,简单来说根据绑定来源(Bindingsource)以及对应的元数据信息而得到,有想看源码细节的童鞋,可将如下图下载放大后去看。
自定义模型绑定
简单讲了下模型绑定原理,更多细节参看上述图查看,接下来我们动手实践下,通过上述从整体上的讲解,我们知道要想实现自定义模型绑定,我们必须实现两个接口,实现IModelBinderProvider接口来实例化ModelBinder,实现IModelBinder接口来将参数进行绑定,最后呢,将我们自定义实现的ModelBinderProvider添加到MVC框架选项中的ModelBinderProvider集合中去。首先我们定义如下类:
public class Employee { [Required] public decimal Salary { get; set; } }
我们定义一个员工类,员工有薪水,如果公司遍布于全世界各地,所以对于各国的币种不一样,假设是中国员工,则币种为人民币,假设一名中国员工薪水为10000人民币,我们想要将【¥10000】绑定到Salary属性上,此时我们通过Postman模拟请求看看。