Asp.net web Api源碼分析-HttpParameterBinding


接着上文Asp.net web Api源碼分析-Filter 我們提到filter的獲取和調用,后面通過HttpActionBinding actionBinding = actionDescriptor.ActionBinding;來獲取HttpActionBinding實例,然后調用 HttpActionBinding的ExecuteBindingAsync方法來綁定Action參數。HttpActionDescriptor中 定義了ActionBinding屬性,默認的實現代碼:

 ServicesContainer controllerServices = _controllerDescriptor.Configuration.Services;
 IActionValueBinder actionValueBinder = controllerServices.GetActionValueBinder();
 HttpActionBinding actionBinding = actionValueBinder.GetBinding(this);

這里的actionValueBinder默認就是一個DefaultActionValueBinder實例,然后調用它的GetBinding方法。然我們看看DefaultActionValueBinder的GetBinding方法:

  public virtual HttpActionBinding GetBinding(HttpActionDescriptor actionDescriptor)
        {
            if (actionDescriptor == null)
            {
                throw Error.ArgumentNull("actionDescriptor");
            }

            HttpParameterDescriptor[] parameters = actionDescriptor.GetParameters().ToArray();
            HttpParameterBinding[] binders = Array.ConvertAll(parameters, GetParameterBinding);

            HttpActionBinding actionBinding = new HttpActionBinding(actionDescriptor, binders);

            EnsureOneBodyParameter(actionBinding);


            return actionBinding;
        }

這里首先獲取當前HttpActionDescriptor的參數集合ReflectedHttpParameterDescriptor[],然后依次調用GetParameterBinding方法把當前HttpActionDescriptor轉化為HttpParameterBinding,GetParameterBinding方法如下:

  protected virtual HttpParameterBinding GetParameterBinding(HttpParameterDescriptor parameter)
        {
            // Attribute has the highest precedence
            // Presence of a model binder attribute overrides.
            ParameterBindingAttribute attr = parameter.ParameterBinderAttribute;
            if (attr != null)
            {
                return attr.GetBinding(parameter);
            }

            // No attribute, so lookup in global map.
            ParameterBindingRulesCollection pb = parameter.Configuration.ParameterBindingRules;
            if (pb != null)
            {
                HttpParameterBinding binding = pb.LookupBinding(parameter);
                if (binding != null)
                {
                    return binding;
                }
            }

            // Not explicitly specified in global map or attribute.
            // Use a default policy to determine it. These are catch-all policies. 
            Type type = parameter.ParameterType;
            if (TypeHelper.IsSimpleUnderlyingType(type) || TypeHelper.HasStringConverter(type))
            {
                // For simple types, the default is to look in URI. Exactly as if the parameter had a [FromUri] attribute.
                return parameter.BindWithAttribute(new FromUriAttribute());
            }

            // Fallback. Must be a complex type. Default is to look in body. Exactly as if this type had a [FromBody] attribute.
            attr = new FromBodyAttribute();
            return attr.GetBinding(parameter);
        }

 這里我們首先調用 ParameterBindingAttribute attr = parameter.ParameterBinderAttribute;來獲取ParameterBindingAttribute實例,然后調用它的 GetBinding方法來獲取HttpParameterBinding,我們還是來看看ParameterBinderAttribute是如何定義 了吧:

在HttpParameterDescriptor中的ParameterBinderAttribute屬性定義如下:

 public virtual ParameterBindingAttribute ParameterBinderAttribute
        {
            get
            {
                if (_parameterBindingAttribute == null)
                {
                    if (!_searchedModelBinderAttribute)
                    {
                        _searchedModelBinderAttribute = true;
                        _parameterBindingAttribute = FindParameterBindingAttribute();
                    }

                }

                return _parameterBindingAttribute;
            }

            set { _parameterBindingAttribute = value; }
        }

FindParameterBindingAttribute其實就是查找參數或則參數類型的ParameterBindingAttribute特性,如果這里取到的ParameterBindingAttribute屬性多余一個則最后要拋出異常,具體這里就不說了。

回到DefaultActionValueBinder中GetParameterBinding方法,找不到 ParameterBindingAttribute對象,我們就 parameter.Configuration.ParameterBindingRules來獲取 ParameterBindingRulesCollection實例,其中 ParameterBindingRules=DefaultActionValueBinder.GetDefaultParameterBinders(), 其中GetDefaultParameterBinders方法實現如下:

 internal static ParameterBindingRulesCollection GetDefaultParameterBinders()
        {
            ParameterBindingRulesCollection pb = new ParameterBindingRulesCollection();

            pb.Add(typeof(CancellationToken), parameter => new CancellationTokenParameterBinding(parameter));
            pb.Add(typeof(HttpRequestMessage), parameter => new HttpRequestParameterBinding(parameter));

            // Warning binder for HttpContent.
            pb.Add(parameter => typeof(HttpContent).IsAssignableFrom(parameter.ParameterType) ?
                                    parameter.BindAsError(Error.Format(SRResources.ParameterBindingIllegalType, parameter.ParameterType.Name, parameter.ParameterName))
                                    : null);

            return pb;
        }

這里的ParameterBindingRulesCollection實例有3個成員,然后調用 ParameterBindingRulesCollection的LookupBinding方法來獲取HttpParameterBinding實 例,其中LookupBinding方法如下:

public class ParameterBindingRulesCollection : Collection<Func<HttpParameterDescriptor, HttpParameterBinding>>
{
     private static Func<HttpParameterDescriptor, HttpParameterBinding> TypeCheck(Type type, Func<HttpParameterDescriptor, HttpParameterBinding> func)
    {
        return (param => (param.ParameterType == type) ? func(param) : null);
    }
    public void Add(Type typeMatch, Func<HttpParameterDescriptor, HttpParameterBinding> funcInner)
    {
        Add(TypeCheck(typeMatch, funcInner));
    }
    public HttpParameterBinding LookupBinding(HttpParameterDescriptor parameter)
    {
        foreach (Func<HttpParameterDescriptor, HttpParameterBinding> func in this)
        {
            HttpParameterBinding binding = func(parameter);

            if (binding != null)
            {
                 return binding;
            }
        }
        return null;
    }
}

所以這里默認的ParameterBindingRulesCollection3個成員是不會返回HttpParameterBinding實例。

如果參數類型是一個簡單類型,並且該類型可以轉化為string類型,然后調用  return parameter.BindWithAttribute(new FromUriAttribute());返回HttpParameterBinding,BindWithAttribute方法其實就是調用ParameterBindingAttributeGetBinding方法,這里默認FromUriAttribute的GetBinding方法,這里FromUriAttribute的繼承數如下:FromUriAttribute-》ModelBinderAttribute-》ParameterBindingAttribute。這里的

如果綁定的參數數據類型比較特殊,那么這里我們就調用FromBodyAttribute的GetBinding方法來獲取HttpParameterBinding實例,這里的FromBodyAttribute繼承樹如下:

FromBodyAttribute-》ParameterBindingAttribute

在這里我們總結一下這里找HttpParameterBinding的順序,(1)parameter.ParameterBinderAttribute實際就是找參數或參數類型的ParameterBindingAttribute屬性,(2)

 parameter.Configuration.ParameterBindingRules 從全局的ParameterBindingRules中找HttpParameterBinding,(3)如果參數類型是一個簡單類型且可以轉化為 string那么我們調用parameter.BindWithAttribute(new FromUriAttribute()),(4)最后我們調用FromBodyAttribute的GetBinding方法來獲取 HttpParameterBinding實例

現在我們回到DefaultActionValueBinder的GetBinding方法中來,現在我們已經獲取到HttpParameterBinding集合,接下里創建一個HttpActionBinding實例,最后調用EnsureOneBodyParameter來檢查HttpActionBinding的ParameterBindings集合是否有2個都需要讀取form表單,如果是則拋出異常。到這里HttpActionDescriptor的ActionBinding的創建也就很清楚了。

回到ApiController的ExecuteAsync方法中來,這里繼續調用HttpActionBinding的ExecuteBindingAsync方法,這里的ExecuteBindingAsync方法實現如下:

  public virtual Task ExecuteBindingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
        {
            if (_parameterBindings.Length == 0)
            {
                return TaskHelpers.Completed();
            }


            // First, make sure the actionBinding is valid before trying to execute it. This keeps us in a known state in case of errors.
            foreach (HttpParameterBinding parameterBinder in ParameterBindings)
            {
                if (!parameterBinder.IsValid)
                {
                    // Throwing an exception because the webService developer's action signature is bad.
                    // This exception will be caught and converted into a 500 by the dispatcher
                    return TaskHelpers.FromError(new InvalidOperationException(parameterBinder.ErrorMessage));
                }
            }

            if (_metadataProvider == null)
            {
                HttpConfiguration config = actionContext.ControllerContext.Configuration;
                _metadataProvider = config.Services.GetModelMetadataProvider();
            }

            // Execute all the binders.
            IEnumerable<Task> tasks = from parameterBinder in ParameterBindings select parameterBinder.ExecuteBindingAsync(_metadataProvider, actionContext, cancellationToken);
            return TaskHelpers.Iterate(tasks, cancellationToken, disposeEnumerator: false);
        }

這個方法邏輯很簡單,如果ParameterBindings沒有成員則直接返回,如果有則需要依次驗證他們的IsValid,然后再依次調用他們的ExecuteBindingAsync方法,ExecuteBindingAsync方法的具體執行我們這里就不多說了。這里我們看看metadataProvider 是個什么東東吧,     SetSingle<ModelMetadataProvider>(new DataAnnotationsModelMetadataProvider());這里我們就知道metadataProvider 其實是一個DataAnnotationsModelMetadataProvider實例,其構造函數也沒什么特別的。這里返回的Task的具體實現我就不多說了,里面用到了一個TaskCompletionSource類,具體的使用我這里一而不說了。

 


免責聲明!

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



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