模型绑定
作者:互联网
我们知道,一个Asp.Net MVC应用的请求总是指向定义在目标Controller类中的某个Action,当Controller被激活之后,这个Action方法会被执行。而大部分的Action方法都有参数,所以MVC在调用目标Action之前必须从请求中提取相应的数据并以此来生成参数,这个过程就是“模型绑定”。
MVC利用一个名为ValueProvider的对象来为Model绑定原始数据,该类实现了IValueProvider接口。
namespace System.Web.Mvc { // // 摘要: // 定义 ASP.NET MVC 中的值提供程序所需的方法。 public interface IValueProvider { // // 摘要: // 确定集合是否包含指定的前缀。 // // 参数: // prefix: // 要搜索的前缀。 // // 返回结果: // 如果集合包含指定的前缀,则为 true;否则为 false。 bool ContainsPrefix(string prefix); // // 摘要: // 使用指定的键来检索值对象。 // // 参数: // key: // 要检索的值对象的键。 // // 返回结果: // 指定键所对应的值对象;如果找不到该键,则为 null。 ValueProviderResult GetValue(string key); } }
namespace System.Web.Mvc { // // 摘要: // 表示将一个值(如窗体发布或查询字符串中的值)绑定到操作方法参数属性或绑定到该参数本身的结果。 public class ValueProviderResult { // // 摘要: // 使用指定的原始值、尝试的值和区域性信息初始化 System.Web.Mvc.ValueProviderResult 类的新实例。 // // 参数: // rawValue: // 原始值。 // // attemptedValue: // 尝试的值。 // // culture: // 区域性。 public ValueProviderResult(object rawValue, string attemptedValue, CultureInfo culture); // // 摘要: // 初始化 System.Web.Mvc.ValueProviderResult 类的新实例。 protected ValueProviderResult(); // // 摘要: // 获取或设置要转换为字符串,以便显示的原始值。 // // 返回结果: // 原始值。 public string AttemptedValue { get; protected set; } // // 摘要: // 获取或设置区域性。 // // 返回结果: // 区域性。 public CultureInfo Culture { get; protected set; } // // 摘要: // 获取或设置值提供程序所提供的原始值。 // // 返回结果: // 原始值。 public object RawValue { get; protected set; } // // 摘要: // 将此结果封装的值转换为指定的类型。 // // 参数: // type: // 目标类型。 // // 返回结果: // 转换后的值。 // // 异常: // T:System.ArgumentNullException: // type 参数为 null。 public object ConvertTo(Type type); // // 摘要: // 使用指定的区域性信息将此结果封装的值转换为指定的类型。 // // 参数: // type: // 目标类型。 // // culture: // 要在转换中使用的区域性。 // // 返回结果: // 转换后的值。 // // 异常: // T:System.ArgumentNullException: // type 参数为 null。 public virtual object ConvertTo(Type type, CultureInfo culture); } }
同时MVC提供了一系列的ValueProvider来处理相应的绑定,当请求过来的时候MVC会通过ValueProviderFactories注册ValueProviderFactory,再创建ValueProvider。
虽然MVC提供的ValueProviderFactory基本上满足大部分Model绑定需求,但是在一些特殊场景下我们可以自定义ValueProviderFactory。
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; using System.Linq; using System.Net.Http.Headers; using System.Web; using System.Web.Http.Controllers; using System.Web.Mvc; namespace Jesen.Web.Mvc { /// <summary> /// 自定义http请求头的ValueProviderFactory来绑定Header的值 /// </summary> public class HttpHeaderValueProviderFactory : ValueProviderFactory { public override IValueProvider GetValueProvider(ControllerContext controllerContext) { NameValueCollection requestData = new NameValueCollection(); NameValueCollection headers = controllerContext.RequestContext.HttpContext.Request.Headers; foreach (string key in headers.Keys) { requestData.Add(key.Replace("-", ""), headers[key]); } return new NameValueCollectionValueProvider(requestData, CultureInfo.InvariantCulture); } } public class CustomHttpHeaders { public string Connection { get; set; } public string Accept { get; set; } public string AcceptCharset { get; set; } public string AcceptLanguage { get; set; } public string Host { get; set; } public string UserAgent { get; set; } } }
public class CustomController : Controller { // GET: Custom public ActionResult Index(CustomHttpHeaders headers) { return View(headers); } }
然后需要在 Application_Start 把我们自定义的ValueProvider添加到ValueProviderFactories中
ValueProviderFactories.Factories.Add(new HttpHeaderValueProviderFactory());
访问该Action得到结果
ValueProvider为构建参数提供了原始数据,而真正的模型绑定工作则是由ModelBinder来完成的。ModelBinder是Model绑定系统最为核心的一个对象。它实现了IModelBinder接口,该接口提供了一个BindModel方法,绑定的数据对象就是通过执行这个方法获得的。
MVC提供了ByteArrayModelBinder、LinqBinaryModelBinder、HttpPostedFileBaseModelBinder、CancellationTokenModelBinder四种针对模型绑定的特殊类型,同时也提供了一个默认的DefaultModelBinder,当根据目标数据类型无法找到一个匹配的ModelBinder时,最终会选择DefaultModelBinder。
和ValueProvider一样,MVC也提供了ModelBinderFactories来注册ModelBinderFactory,然后创建ModelBinder,我们也可以自定义我们自己的ModelBinder。
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; using System.Linq; using System.Net.Http.Headers; using System.Web; using System.Web.Http.Controllers; using System.Web.Mvc; namespace Jesen.Web.Mvc { public class Person { public int Id { get; set; } public string Name { get; set; } } /// <summary> /// 自定义ModelBinder /// </summary> public class PersonModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { return new Person(); } } /// <summary> /// 自定义ModelBinderProvider来控制采用的ModelBinder /// </summary> public class PersonModelBinderProvider : IModelBinderProvider { public IModelBinder GetBinder(Type modelType) { if (typeof(Person).IsAssignableFrom(modelType)) { return new PersonModelBinder(); } return null; } } }
public class CustomController : Controller { // GET: Custom public ActionResult Index(Person p) { ControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(typeof(CustomController)); ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(ControllerContext, "Index"); Dictionary<ParameterDescriptor, IModelBinder> binders = new Dictionary<ParameterDescriptor, IModelBinder>(); foreach(ParameterDescriptor parameterDescriptor in actionDescriptor.GetParameters()) { binders.Add(parameterDescriptor, this.GetModelBinder(parameterDescriptor)); } return View(binders); } /// <summary> /// 调用ActionInvoker的GetModelBinder方法得到针对指定参数的ModelBinder对象 /// </summary> /// <param name="parameterDescriptor"></param> /// <returns></returns> private IModelBinder GetModelBinder(ParameterDescriptor parameterDescriptor) { MethodInfo getModelBinder = typeof(ControllerActionInvoker).GetMethod("GetModelBinder", BindingFlags.Instance | BindingFlags.NonPublic); return (IModelBinder)getModelBinder.Invoke(this.ActionInvoker, new object[] { parameterDescriptor }); } }
@model Dictionary<ParameterDescriptor, IModelBinder> @{ Layout = null; } <html> <body> @foreach(var item in Model) { <p>@item.Key.ParameterName</p> <p>@item.Value.GetType().Name</p> } </body> </html>
同样需要在Application_Start中添加自定义的ModelBinderFactory
ModelBinderProviders.BinderProviders.Add(new PersonModelBinderProvider());
运行代码可以看到模型绑定器变成了我们定义的PersonModelBinder了
除了上述方法自定义ModelBinderProvider来为某种具体的数据类型提供对应的ModelBinder外,还可以使用ModelBinders静态类型直接注册数据类型与对应ModelBinder之间的匹配关系,如果同时指定了这两种方式,那么ModelBinderProvider的方式优先级更高。
ModelBinderProviders.BinderProviders.Add(new PersonModelBinderProvider()); ModelBinders.Binders.Add(typeof(Person), new PersonModelBinder());
MVC提供的第三种定义匹配ModelBinder的方式可以通过ModelBinderAttribute来实现
[ModelBinder(typeof(PersonModelBinder))] public class Person { public int Id { get; set; } public string Name { get; set; } }
public ActionResult Index([ModelBinder(typeof(PersonModelBinder))] Person p) { ControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(typeof(CustomController)); ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(ControllerContext, "Index"); Dictionary<ParameterDescriptor, IModelBinder> binders = new Dictionary<ParameterDescriptor, IModelBinder>(); foreach(ParameterDescriptor parameterDescriptor in actionDescriptor.GetParameters()) { binders.Add(parameterDescriptor, this.GetModelBinder(parameterDescriptor)); } return View(binders); }
那么如果同时采用了三种方式,它们的优先级是: 参数中使用特性 --> ModelProviderFactory --> 静态ModelBinders --> 类上的特性。
最后,笔者在工作中开发App接口时,采用RSA方式对参数进行加密,自定义了自己的ModelBinder。其代码如下:
public class EncryptedModelBinder : DefaultModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (controllerContext.RouteData.DataTokens["area"] != null) { if (String.Equals("Examination", controllerContext.RouteData.DataTokens["area"].ToString(), StringComparison.OrdinalIgnoreCase)) { return base.BindModel(controllerContext, bindingContext); } } RuntimeHelpers.EnsureSufficientExecutionStack(); if (bindingContext == null) { throw new ArgumentNullException("bindingContext"); } if (string.IsNullOrEmpty(bindingContext.ModelName) || bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); object rawValue= (valueProviderResult.RawValue as Array).GetValue(0); string decryptedString = null; if ((String.Equals("Product", controllerContext.RouteData.Values["controller"].ToString(), StringComparison.OrdinalIgnoreCase) && string.Equals("Validate", controllerContext.RouteData.Values["action"].ToString(), StringComparison.OrdinalIgnoreCase)) || (String.Equals("logistics", controllerContext.RouteData.Values["controller"].ToString(), StringComparison.OrdinalIgnoreCase) && string.Equals("enquiryvalidate", controllerContext.RouteData.Values["action"].ToString(), StringComparison.OrdinalIgnoreCase)) || (String.Equals("logistics", controllerContext.RouteData.Values["controller"].ToString(), StringComparison.OrdinalIgnoreCase) && string.Equals("enquiry", controllerContext.RouteData.Values["action"].ToString(), StringComparison.OrdinalIgnoreCase))) { decryptedString = rawValue.ToString(); } else if ((String.Equals("RealName", controllerContext.RouteData.Values["controller"].ToString(), StringComparison.OrdinalIgnoreCase) && string.Equals("Input", controllerContext.RouteData.Values["action"].ToString(), StringComparison.OrdinalIgnoreCase)) || (String.Equals("Home", controllerContext.RouteData.Values["controller"].ToString(), StringComparison.OrdinalIgnoreCase) && string.Equals("ReplyLeaveMessage", controllerContext.RouteData.Values["action"].ToString(), StringComparison.OrdinalIgnoreCase))) { if (String.Equals("PhotoSelf", bindingContext.ModelName, StringComparison.OrdinalIgnoreCase) || String.Equals("PhotoIdFront", bindingContext.ModelName, StringComparison.OrdinalIgnoreCase) || String.Equals("PhotoIdBack", bindingContext.ModelName, StringComparison.OrdinalIgnoreCase) || String.Equals("File", bindingContext.ModelName, StringComparison.OrdinalIgnoreCase)) { decryptedString = rawValue.ToString(); } else { decryptedString = RSACryption.RSADecrypt(RSACryption.strPrivateKey, rawValue.ToString(), "utf-8");//rawValue + ""; //解密,update by hujs 2016-12-01 } } //else if (String.Equals("Examination", controllerContext.RouteData.DataTokens["area"].ToString(), StringComparison.OrdinalIgnoreCase)) //{ // decryptedString = rawValue.ToString(); //} else { if (String.Equals("Home", controllerContext.RouteData.Values["controller"].ToString(), StringComparison.OrdinalIgnoreCase) && string.Equals("InfoModify", controllerContext.RouteData.Values["action"].ToString(), StringComparison.OrdinalIgnoreCase) && bindingContext.ModelName == "Signature") { decryptedString = rawValue.ToString(); } else { decryptedString = RSACryption.RSADecrypt(RSACryption.strPrivateKey, rawValue.ToString(), "utf-8");//rawValue + ""; //解密,update by hujs 2016-12-01 } } //string decryptedString = RSACryption.RSADecrypt(RSACryption.strPrivateKey, rawValue.ToString(), "utf-8");//rawValue + ""; //解密,update by hujs 2016-12-01 valueProviderResult = new ValueProviderResult(decryptedString, decryptedString, valueProviderResult.Culture); if (valueProviderResult != null) { return BindSimpleModel(controllerContext, bindingContext, valueProviderResult); } } return base.BindModel(controllerContext, bindingContext); } internal object BindSimpleModel(ControllerContext controllerContext, ModelBindingContext bindingContext, ValueProviderResult valueProviderResult) { bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); if (bindingContext.ModelType.IsInstanceOfType(valueProviderResult.RawValue)) { return valueProviderResult.RawValue; } if (bindingContext.ModelType != typeof(string)) { if (bindingContext.ModelType.IsArray) { return ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType); } Type type =ExtractGenericInterface(bindingContext.ModelType, typeof(IEnumerable<>)); if (type != null) { object o = this.CreateModel(controllerContext, bindingContext, bindingContext.ModelType); Type collectionType = type.GetGenericArguments()[0]; Type destinationType = collectionType.MakeArrayType(); object newContents = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, destinationType); if (typeof(ICollection<>).MakeGenericType(new Type[] { collectionType }).IsInstanceOfType(o)) { CollectionHelpers.ReplaceCollection(collectionType, o, newContents); } return o; } } return ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType); } private static object ConvertProviderResult(ModelStateDictionary modelState, string modelStateKey, ValueProviderResult valueProviderResult, Type destinationType) { try { return valueProviderResult.ConvertTo(destinationType); } catch (Exception exception) { modelState.AddModelError(modelStateKey, exception); return null; } } public static Type ExtractGenericInterface(Type queryType, Type interfaceType) { if (MatchesGenericType(queryType, interfaceType)) { return queryType; } return MatchGenericTypeFirstOrDefault(queryType.GetInterfaces(), interfaceType); } private static bool MatchesGenericType(Type type, Type matchType) { return (type.IsGenericType && (type.GetGenericTypeDefinition() == matchType)); } private static Type MatchGenericTypeFirstOrDefault(Type[] types, Type matchType) { for (int i = 0; i < types.Length; i++) { Type type = types[i]; if (MatchesGenericType(type, matchType)) { return type; } } return null; } private static class CollectionHelpers { private static readonly MethodInfo _replaceCollectionMethod = typeof(EncryptedModelBinder.CollectionHelpers).GetMethod("ReplaceCollectionImpl", BindingFlags.NonPublic | BindingFlags.Static); private static readonly MethodInfo _replaceDictionaryMethod = typeof(EncryptedModelBinder.CollectionHelpers).GetMethod("ReplaceDictionaryImpl", BindingFlags.NonPublic | BindingFlags.Static); [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] public static void ReplaceCollection(Type collectionType, object collection, object newContents) { _replaceCollectionMethod.MakeGenericMethod(new Type[] { collectionType }).Invoke(null, new object[] { collection, newContents }); } } }
标签:return,string,bindingContext,模型,绑定,System,controllerContext,public 来源: https://www.cnblogs.com/jesen1315/p/11015178.html