本篇中会为大家介绍在ASP.NET Web API中ModelBinder的绑定原理以及涉及到的一些对象模型,还有简单的Model绑定示例,在前面的篇幅中讲解了Model元数据、ValuePRovider的模块,然后还有本篇的Model绑定的模块这些会结合到后面篇幅中的ParameterBinder模块中来使用,也就是说在ASP.NET Web API框架中绑定的方式有两种实现,都是通过ParameterBinder来对参数进行绑定,而在ParameterBinder中的实现则会有两种方式,今天就给大家单独的说明一下Model绑定,把它看成一个单独的功能模块就行了。
不瞎扯了,直接进入主题,首先我们来看IModelBinder接口类型的定义,所有的ModelBinder功能模块都实现了IModelBinder接口,如示例代码1-1
示例代码1-1
IModelBinder
namespace System.Web.Http.ModelBinding{ public interface IModelBinder { bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext); }}
在代码1-1中我们可以看到BindModel()方法中定义了两个参数而且都是上下文类型的参数,第一个上下文参数表示操作上下文,它是在控制器方法被执行之前就被创建并且其中封装了一些后续操作必要的信息以及存储请求、响应和参数绑定的结果值,这个稍后会跟大家讲解,它起到一个很重要的作用。
第二个上下文参数是绑定上下文参数,这个容易理解,意思就是对象里封装着本次要绑定对象的信息也就是Model元数据、ValueProvider之类的信息,现在不理解也没关系慢慢往后看看完就会明白的。
HttpActionContext
代码1-2
namespace System.Web.Http.Controllers{ public class HttpActionContext { public HttpActionContext(); public HttpActionContext(HttpControllerContext controllerContext, HttpActionDescriptor actionDescriptor); public Dictionary<string, object> ActionArguments { get; } public HttpActionDescriptor ActionDescriptor { get; set; } public HttpControllerContext ControllerContext { get; set; } public ModelStateDictionary ModelState { get; } public HttpRequestMessage Request { get; } public HttpResponseMessage Response { get; set; } }}
代码1-2就是HttpActionContext类型的定义了,下面简单的描述一下几个属性所表示的含义,ActionArguments属性中的值是对应着控制器方法的参数列表,其中Key值就是参数名称,Value值就是参数的实际数据值了,因为Model绑定是一个递归的过程在复杂类型的子项绑定完毕后并不会对这个属性进行赋值,而是等这一个参数项全部绑定完成了才会进行赋值。
HttpActionDescriptor类型的ActionDescriptor属性,这个是HttpControllerDescriptor类型之后所见的第二个这种描述类型,后面还会有HttpParameterDescriptor类型,在这里ActionDescriptor属性中就是封装着当前所要请求的控制器方法信息,类似封装着方法的元数据信息。
ControllerContext属性就不用多说了想必大家也都知道它的作用,ModelStateDictionary类型的ModelState属性则是在Model绑定之后才会对其操作,是把参数绑定验证后的值存在这个属性当中。
HttpActionContextExtensions
代码1-3
namespace System.Web.Http.Controllers{ // 摘要: // 包含 System.Web.Http.Controllers.HttpActionContext 的扩展方法。 [EditorBrowsable(EditorBrowsableState.Never)] public static class HttpActionContextExtensions { public static bool Bind(this HttpActionContext actionContext, ModelBindingContext bindingContext); public static bool Bind(this HttpActionContext actionContext, ModelBindingContext bindingContext, IEnumerable<IModelBinder> binders); //…… }}
代码1-3的所示的是包含HttpActionContext类型的扩展方法类型HttpActionContextExtensions,我们在这之中可以看到两个Bind()方法,这两个Bind()也是Model绑定至关重要的地方,因为Model绑定的递归就是在这里实现的,至于怎么实现的稍后会说。
这里的第一个Bind()方法其实就是调用第二个Bind()方法来执行的。而第二Bind()方法中IEnumerable<IModelBinder>参数则是从HttpActionContext类型中获取到当前的HttpControllerContext并且再从其中获取到当前请求的配置对象HttpConfiguration对象,最后从配置对象中的容器属性中获取ModelBinder的提供程序集合,然后根据当前ModelBindingContext中的ModelType类型使用提供程序集合来判断后获取适合类型的IModelBinder集合,从而调用第二个Bind()方法。
这样看可能还是不太理解递归的情况,大家稍安勿躁,后面慢慢讲解。
ModelBindingContext
代码1-4
namespace System.Web.Http.ModelBinding{ // 摘要: // 提供运行模型联编程序的上下文。 public class ModelBindingContext { // 摘要: // 初始化 System.Web.Http.ModelBinding.ModelBindingContext 类的新实例。 public ModelBindingContext(); public ModelBindingContext(ModelBindingContext bindingContext); public bool FallbackToEmptyPrefix { get; set; } public object Model { get; set; } public ModelMetadata ModelMetadata { get; set; } public string ModelName { get; set; } public ModelStateDictionary ModelState { get; set; } public Type ModelType { get; } public IDictionary<string, ModelMetadata> PropertyMetadata { get; } public ModelValidationNode ValidationNode { get; set; } public IValueProvider ValueProvider { get; set; } }}
代码1-4中所示的就是绑定上下文对象,首先我们看到它的重载构造函数中有个ModelBindingContext类型的参数用以表示嵌套,内部的实现是用以传递ModelState属性的状态值和ValueProvider值提供程序,至于为什么是这种结构?这个跟绑定复杂类型的时候有关,构造就如同ModelState属性对象的ModelStateDictionary类型一样,这种结构稍后会讲解。
当中的Model属性表示当前ModelBindingContext中绑定过后的Model值,然后ModelMetadata、ModelName、ModelType、PropertyMetadata这些属性都是表示当前ModelBindingContext中Model的对应值。这个”当前”可能是string类型,也可能是复杂类型。(复杂类型在绑定的时候会被ASP.NET Web API框架封装起来有个特定的类型,这个稍后讲解)
简单类型绑定器- TypeConverterModelBinder
代码1-5
public sealed class TypeConverterModelBinder : IModelBinder { // Methods public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) { object obj2; ModelBindingHelper.ValidateBindingContext(bindingContext); ValueProviderResult result = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (result == null) { return false; } bindingContext.ModelState.SetModelValue(bindingContext.ModelName, result); try { obj2 = result.ConvertTo(bindingContext.ModelType); } catch (Exception exception) { if (IsFormatException(exception)) { string errorMessage = ModelBinderConfig.TypeConversionErrorMessageProvider(actionContext, bindingContext.ModelMetadata, result.AttemptedValue); if (errorMessage != null) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, errorMessage); } } else { bindingContext.ModelState.AddModelError(bindingContext.ModelName, exception); } return false; } ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref obj2); bindingContext.Model = obj2; return true; }}
在代码1-5中,我们看到TypeConverterModelBinder类型实现了IModelBinder接口,并且在BindModel()方法中直接就是使用绑定上下文中的ValueProvider根据绑定上下文中的ModelName属性,ModelName就是我们前面ValueProvider篇幅中讲解到的前缀值和属性值的合并。而后会将获取到的结果值进行类型判断,如果不能正确的转换成string类型则会提示各种异常,当然了这种异常不会报出来,只是添加到了当前绑定上下文的ModelState属性中,如果可以转换成功则会对当前绑定上下文的Model值进行赋值。
简单类型绑定器提供程序- TypeConverterModelBinderProvider
代码1-6
public sealed class TypeConverterModelBinderProvider : ModelBinderProvider { // Methods public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType) { if (modelType == null) { throw Error.ArgumentNull("modelType"); } if (!TypeHelper.HasStringConverter(modelType)) { return null; } return new TypeConverterModelBinder(); } }
代码1-6中所示TypeConverterModelBinderProvider类型则为简单类型绑定器的提供程序,并且继承自ModelBinderProvider类型,讲到这里了我才发现我把这个类型忘记说明了,不过没关系,大家自行看一下就好了,ModelBinderProvider就是一个抽象类,然后定义了一个抽象的行为。
在TypeConverterModelBinderProvider类型的实现中,我们可以清楚的看到如果参数modelType可以成功的转换成string类型则会返回TypeConverterModelBinder类型的实例,不然则返回null。
复杂类型封装对象-ComplexModelDto
代码1-7
namespace System.Web.Http.ModelBinding.Binders{ // 摘要: // 表示一个复杂模型的数据传输对象 (DTO)。 public class ComplexModelDto { public ComplexModelDto(ModelMetadata modelMetadata, IEnumerable<ModelMetadata> propertyMetadata); public ModelMetadata ModelMetadata { get; } public Collection<ModelMetadata> PropertyMetadata { get; } public IDictionary<ModelMetadata, ComplexModelDtoResult> Results { get; } }}
大家也看到了代码1-7中的注释部分,表示一个复杂模型(Model)的数据传输对象,实际就是对复杂类型的重新封装,封装的方式大家也看到了都是以Model元数据的方式,这个类型我就不多说了。对于Model元数据不太清楚的朋友
新闻热点
疑难解答