zoukankan      html  css  js  c++  java
  • 模型绑定

      我们知道,一个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 });
                }
            }
        }
  • 相关阅读:
    HUST 1584 摆放餐桌
    HUST 1585 排队
    HUST 1583 长度单位
    树状数组 poj2352 Stars
    Visual Studio2013应用笔记---WinForm事件中的Object sender和EventArgs e参数
    倒置输入的数 Exercise07_02
    指定等级 Exercise07_01
    检测密码 Exercise06_18
    一年的天数 Exercise06_16
    数列求和 Exercise06_13
  • 原文地址:https://www.cnblogs.com/jesen1315/p/11015178.html
Copyright © 2011-2022 走看看