• 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类,具体的使用我这里一而不说了。

  • 相关阅读:
    Web server failed to start. Port 8080 was already in use.
    org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplic
    HttpClient测试框架
    Mock接口平台Moco学习
    自动化测试框架TestNG
    css常用记录...个人查看使用
    常用JS工具类 .....持续更新中 ...CV大法好
    element-upload覆盖默认行为(多个文件上传调用一次接口)
    基础术语理解
    MVC和MVVM设计模式简单理解
  • 原文地址:https://www.cnblogs.com/majiang/p/2802896.html
Copyright © 2020-2023  润新知