在之前的文章中我们讲过,在获取ModelBinder的时候优先选择的是应用在Action参数上的CustomModelBinder,具体的实现是在静态类ModelBinders里面的GetBinderFromAttributesImpl方法:
private static IModelBinder GetBinderFromAttributesImpl(CustomModelBinderAttribute[] attrs, Func<string> errorMessageAccessor) |
我们在分析源码的时候可以看到ModelBinder有一个Binders属性,是对ModelBinderDictionary类的一个封装,具体如下:
private static readonly ModelBinderDictionary _binders = CreateDefaultBinderDictionary(); public static ModelBinderDictionary Binders {get {return _binders;}} private static ModelBinderDictionary CreateDefaultBinderDictionary() { // We can't add a binder to the HttpPostedFileBase type as an attribute, so we'll just // prepopulate the dictionary as a convenience to users. ModelBinderDictionary binders = new ModelBinderDictionary() { {typeof(HttpPostedFileBase), new HttpPostedFileBaseModelBinder() }, {typeof(byte[]), new ByteArrayModelBinder() }, {typeof(Binary), new LinqBinaryModelBinder() } }; return binders; } |
mvc应用程序中提供了三种默认的ModelBinder供我们使用,我们逐一来看看。HttpPostedFileBaseModelBinder里面的成员如下:
public class HttpPostedFileBaseModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { HttpPostedFileBase theFile = controllerContext.HttpContext.Request.Files[bindingContext.ModelName]; return ChooseFileOrNull(theFile); } // helper that returns the original file if there was content uploaded, null if empty internal static HttpPostedFileBase ChooseFileOrNull(HttpPostedFileBase rawFile) { // case 1: there was no <input type="file" ... /> element in the post if (rawFile == null) {return null;} // case 2: there was an <input type="file" ... /> element in the post, but it was left blank if (rawFile.ContentLength == 0 && String.IsNullOrEmpty(rawFile.FileName)) {return null;} // case 3: the file was posted return rawFile; } } |
HttpPostedFileBaseModelBinder用于客户端上传文件时相关的绑定,看红色的英文注释,不解释。还有就是字节数组的绑定ByteArrayModelBinder,具体实现如下:
public class ByteArrayModelBinder : IModelBinder { public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); // case 1: there was no <input ... /> element containing this data if (valueResult == null) {return null;} string value = valueResult.AttemptedValue; // case 2: there was an <input ... /> element but it was left blank if (String.IsNullOrEmpty(value)) {return null;} // Future proofing. If the byte array is actually an instance of System.Data.Linq.Binary // then we need to remove these quotes put in place by the ToString() method. string realValue = value.Replace("\"", String.Empty); return Convert.FromBase64String(realValue); } } |
还有就是LinqBinaryModelBinder的绑定,该类是继承了字节数组ByteArrayModelBinder类,具体的实现如下:
public class LinqBinaryModelBinder : ByteArrayModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { byte[] byteValue = (byte[])base.BindModel(controllerContext, bindingContext); if (byteValue == null) {return null;} return new Binary(byteValue); } } |
我们之前介绍过mvc中获取ModelBinder的顺序,在Action方法的参数中没有标注CustomModelBinderAttribute和ModelBinderProviders中的BinderProviders中也没有提供ModelBinder的话,就会在全局文件中查找,如同ModelBinderProviders中的BinderProviders在Application_Start注册一样,ModelBinders提供的ModelBinder也是在全局文件的Application_Start注册:
protected void Application_Start() ModelBinders.Binders.Add(***); } |