[Web API] Web API 2 深入系列(7) Model綁定(下)


目錄

  1. ModelBinder

  2. ModelBinderProvider

  3. 不同類型的Model綁定

    • 簡單類型
    • 復雜類型
    • 其他類型

ModelBinder

ModelBinder是Model綁定的核心.

public interface IModelBinder
{
    //綁定Model方法,返回綁定是否成功
    bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext);
}

ModelBindingContext

public class ModelBindingContext
{
    //數據源
    public IValueProvider ValueProvider { get; set; }
    //最終創建的對象 綁定過程就是創建Model
    public object Model { get; set; }
    //參數名稱
    public string ModelName { get; set; }
    //參數類型
    public Type ModelType { get; }

    //參數元數據
    public ModelMetadata ModelMetadata { get; set; }
    //屬性元數據
    public IDictionary<string, ModelMetadata> PropertyMetadata { get; }

    //存儲綁定結果(包括錯誤信息,ValueProviderResult),該值引用自ApiController的ModelState
    public ModelStateDictionary ModelState { get; set; }
    
    public bool FallbackToEmptyPrefix { get; set; }
}

當ModelBinder特性的Name(為null),FallbackToEmptyPrefix為True.
FallbackToEmptyPrefix為False時,則必須數據源有前綴才能綁定上.

Web API定義了一系列的ModelBinder,這里先介紹

public class CompositeModelBinder : IModelBinder
{
    //IModelBinder 集合
    public CompositeModelBinder(params IModelBinder[] binders)
    //使用內部ModelBinder進行綁定Model(默認遍歷一次,如果FallbackToEmptyPrefix為True,則會有2次遍歷機會)
    public virtual bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
}

ModelBinderProvider

Web API通過ModelBinderProvider創建ModelBinder

public abstract class ModelBinderProvider
{
    public abstract IModelBinder GetBinder(HttpConfiguration configuration, Type modelType);
}

Web API中默認注冊了一系列的ModelBinderProvider

    this.SetMultiple<ModelBinderProvider>(new ModelBinderProvider[8]
    {
        (ModelBinderProvider) new TypeConverterModelBinderProvider(),
        (ModelBinderProvider) new TypeMatchModelBinderProvider(),
        (ModelBinderProvider) new KeyValuePairModelBinderProvider(),
        (ModelBinderProvider) new ComplexModelDtoModelBinderProvider(),
        (ModelBinderProvider) new ArrayModelBinderProvider(),
        (ModelBinderProvider) new DictionaryModelBinderProvider(),
        (ModelBinderProvider) new CollectionModelBinderProvider(),
        (ModelBinderProvider) new MutableObjectModelBinderProvider()
    });

對應的我們先介紹一下

//同樣都是組裝一批ModelBinderProvider
public sealed class CompositeModelBinderProvider : ModelBinderProvider
{
    public CompositeModelBinderProvider(IEnumerable<ModelBinderProvider> providers)
    public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType)
}

ModelBinderAttribute除了通過Name設置FallbackToEmptyPrefix,還有個更重要的屬性BinderType

public class ModelBinderAttribute : ParameterBindingAttribute
{
    public string Name { get; set; }
    //用來指定ModelBinder 或 ModelBinderProvider
    public Type BinderType { get; set; }
}

不同類型的Model綁定

不同的數據類型,有不同的數據結構.
所以針對他們的綁定機制也是不同的.

簡單類型

TypeConverterModelBinder

public sealed class TypeConverterModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
        model = valueProviderResult.ConvertTo(bindingContext.ModelType);
        bindingContext.Model = model;
        return true;
    }
}

復雜類型

在介紹復雜類型的Bind前,先介紹一下下面2個類型.

ComplexModelDto包含復雜類型的元數據和綁定結果

public class ComplexModelDto
{
    public ComplexModelDto(ModelMetadata modelMetadata, IEnumerable<ModelMetadata> propertyMetadata)

    public ModelMetadata ModelMetadata { get; private set; }
    public Collection<ModelMetadata> PropertyMetadata { get; private set; }
    //key 為屬性的元數據,value 為綁定結果
    public IDictionary<ModelMetadata, ComplexModelDtoResult> Results { get; private set; }
}

ComplexModelDtoResult

public sealed class ComplexModelDtoResult
{
    //綁定結果值
    public object Model { get; private set; }
    //驗證信息
    public ModelValidationNode ValidationNode { get; private set; }
}

復雜類型的綁定由 MutableObjectModelBinder 和 ComplexModelDtoModelBinder共同完成.

而上面2個類型,實際就是用來在2個ModelBinder間傳遞的對象

public class MutableObjectModelBinder : IModelBinder
{
    public virtual bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        //類型篩選
        if (!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName) || !MutableObjectModelBinder.CanBindType(bindingContext.ModelType))
            return false;
        //1. 創建空對象
        bindingContext.ModelMetadata.Model = Activator.CreateInstance(bindingContext.ModelType);
        //2. 創建ComplexModelDto對象
        ComplexModelDto complexModelDto = new ComplexModelDto(bindingContext.ModelMetadata,bindingContext.PropertyMetadata);
        //3. 進入綁定流程
        this.ProcessDto(actionContext, bindingContext, complexModelDto);
        return true;
    }

    internal void ProcessDto(HttpActionContext actionContext, ModelBindingContext bindingContext, ComplexModelDto dto)
    {
        //創建子ModelBindingContext
        var subContext = new ModelBindingContext(bindingContext)
        {
            ModelName = bindingContext.ModelName,
            ModelMetadata = GetMetadata(typeof(ComplexModelDto))
        };
        //調用ComplexModelDtoModelBinder 對ComplexModelDto進行綁定
        actionContext.Bind(subContext);

        //對復雜類型的屬性進行綁定
        foreach (var result in (IEnumerable<KeyValuePair<ModelMetadata, ComplexModelDtoResult>>) dto.Results)
        {
            ModelMetadata key = result.Key;
            ComplexModelDtoResult dtoResult = result.Value;
            if (dtoResult != null)
            {
                var property = bindingContext.ModelType.GetProperty(key.PropertyName);
                this.SetProperty(actionContext, bindingContext, key, dtoResult,property);
            }
        }
    }
}

ComplexModelDtoModelBinder則將所有屬性再通過調度其他的ModelBinder進行綁定.並將結果保存在ComplexModelDto的Results屬性中.

其他類型

Web API還提供了其他常用的數據類型 ModelBinder

CollectionModelBinder來實現 集合 類型(指的集合是實現了IEnumerable接口的類型)

ArrayModelBinder來實現 數組 類型

DictionaryModelBinder來實現 字典 類型

通過對應的ModelBinderProvider可以知道對應的ModelBinder 能實現哪種類型的綁定

備注


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM