asp.net core mvc系列文章

asp.net core mvc action参数绑定 从使用到

2019-03-01  本文已影响0人  李浩的博客

源码太多,涉及太广,作为使用者其实不用了解那么多,带着问题去看看源码吧。

Q1: 数据是哪来的?

Q1.1 总的来讲,有四种来源的数据

    /// <summary>
    /// Defines the methods that are required for a value provider.
    /// </summary>
    public interface IValueProvider
    {
        /// <summary>
        /// Determines whether the collection contains the specified prefix.
        /// </summary>
        /// <param name="prefix">The prefix to search for.</param>
        /// <returns>true if the collection contains the specified prefix; otherwise, false.</returns>
        bool ContainsPrefix(string prefix);

        /// <summary>
        /// Retrieves a value object using the specified key.
        /// </summary>
        /// <param name="key">The key of the value object to retrieve.</param>
        /// <returns>The value object for the specified key. If the exact key is not found, null.</returns>
        ValueProviderResult GetValue(string key);
    }
 /// <summary>
    /// Reads an object from the request body.
    /// </summary>
    public interface IInputFormatter
    {
        /// <summary>
        /// Determines whether this <see cref="IInputFormatter"/> can deserialize an object of the
        /// <paramref name="context"/>'s <see cref="InputFormatterContext.ModelType"/>.
        /// </summary>
        /// <param name="context">The <see cref="InputFormatterContext"/>.</param>
        /// <returns>
        /// <c>true</c> if this <see cref="IInputFormatter"/> can deserialize an object of the
        /// <paramref name="context"/>'s <see cref="InputFormatterContext.ModelType"/>. <c>false</c> otherwise.
        /// </returns>
        bool CanRead(InputFormatterContext context);

        /// <summary>
        /// Reads an object from the request body.
        /// </summary>
        /// <param name="context">The <see cref="InputFormatterContext"/>.</param>
        /// <returns>A <see cref="Task"/> that on completion deserializes the request body.</returns>
        Task<InputFormatterResult> ReadAsync(InputFormatterContext context);
    }

Q1.2 四种数据源的被应用位置

  class DecimalModelBinder

  public Task BindModelAsync(ModelBindingContext bindingContext)
  {
        var modelName = bindingContext.ModelName;
        var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
  }
 class BodyModelBinder

 public async Task BindModelAsync(ModelBindingContext bindingContext)
        {

                var result = await formatter.ReadAsync(formatterContext);

                if (result.HasError)
                {
                }

                if (result.IsModelSet)
                {
                }
                else
                {
                }
        }

Q1.3 什么时候用值提供器,什么时候用解析器

值提供器



解析器


Q1.4 哪些绑定器的数据源是值提供器


Q1.5 哪些绑定器的数据源是解析器


Q1.6 怎么确定该用哪个绑定器

关键代码

             //这里会获得一个绑定器
            IModelBinder result = null;

            for (var i = 0; i < _providers.Length; i++)
            {
                var provider = _providers[i];
                result = provider.GetBinder(providerContext);
                if (result != null)
                {
                    break;
                }
            }


//这里是BodyModelBinder GetBinder的逻辑
public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            //是否BindingSource 是BindingSource.Body
            if (context.BindingInfo.BindingSource != null &&
                context.BindingInfo.BindingSource.CanAcceptDataFrom(BindingSource.Body))
            {
                if (_formatters.Count == 0)
                {
                    throw new InvalidOperationException(Resources.FormatInputFormattersAreRequired(
                        typeof(MvcOptions).FullName,
                        nameof(MvcOptions.InputFormatters),
                        typeof(IInputFormatter).FullName));
                }

                return new BodyModelBinder(_formatters, _readerFactory, _loggerFactory, _options);
            }

            return null;
        }

//这里是SimpleTypeModelBinderProvider GetBinder的逻辑
        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (!context.Metadata.IsComplexType)//是否是复杂类型
            {
                var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
                return new SimpleTypeModelBinder(context.Metadata.ModelType, loggerFactory);
            }

            return null;
        }

不同的绑定器有不同的判断条件,总的讲逻辑就是,根据ModelBinderProviderContext 判断我能不能行。


Q1.7 6大From特性的作用就是影响绑定器的 “判断条件”

每个From特性有不同的BindingSource,绑定器提供器构建绑定器的时候,会判断BindingSource。

eg:

    public class FromHeaderAttribute : Attribute, IBindingSourceMetadata, IModelNameProvider
    {
        /// <inheritdoc />
        public BindingSource BindingSource => BindingSource.Header;

        /// <inheritdoc />
        public string Name { get; set; }
    }
       public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            var bindingInfo = context.BindingInfo;
            if (bindingInfo.BindingSource == null ||
                !bindingInfo.BindingSource.CanAcceptDataFrom(BindingSource.Header))
            {
                return null;
            }

            return new HeaderModelBinder(loggerFactory);
            }
}

Q1.8 为啥application/json提交的时候,不加FromBody绑定不了Model

1. 简单类型,默认的绑定器是什么?

action

        public IActionResult Index(string a)
        {
            return View();
        }

获取绑定器

QQ截图20190228132304.png

2. 绑定Model的时候,默认的绑定器是什么?

action 定义

    public class HomeController : Controller
    {
        public IActionResult Index(Test a)
        {
            return View();
        }
    }
    public class Test
    {
        public string a { get; set; }
    }

顶层复杂类型绑定器,绑定器是一个树形结构

QQ截图20190228135851.png

复杂类型绑定器中根据属性描述,生成属性对应的绑定器。绑定器是一个树形结构。

public class ComplexTypeModelBinderProvider : IModelBinderProvider
    {
        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
            {
                var propertyBinders = new Dictionary<ModelMetadata, IModelBinder>();
                for (var i = 0; i < context.Metadata.Properties.Count; i++)
                {
                    //根据属性描述,生成属性对应的绑定器。绑定器是一个树形结构。
                    var property = context.Metadata.Properties[i];
                    propertyBinders.Add(property, context.CreateBinder(property));
                }

                var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
                return new ComplexTypeModelBinder(propertyBinders, loggerFactory);
            }

            return null;
        }
    }

绑定属性的依然是简单绑定器

QQ截图20190228141052.png

复杂类型绑定器中的属性绑定器有哪几种

就是上面提到的“绑定器的数据源是值提供器”


也就是说,默认情况下,绑定的数据来源都是值提供器。而 application/json是需要解析器解析数据,数据源是解析器,所以,默认情况下,application/json的json数据是无法绑定到model上的。

3. FromModel 特性设置后,为啥可以绑定application/json的数据了

    public class HomeController : Controller
    {
        public IActionResult Index([FromBody]Test a)
        {
            return View();
        }
    }
    public class Test
    {
        public string a { get; set; }
    }

设置后,默认绑定器变成了


QQ截图20190228142655.png

为啥默认绑定器变成了BodyModelBinder

看看BodyModelBinderProvider中的GetBinder的逻辑就知道了

public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.BindingInfo.BindingSource != null &&
                context.BindingInfo.BindingSource.CanAcceptDataFrom(BindingSource.Body))
            {
                if (_formatters.Count == 0)
                {
                    throw new InvalidOperationException(Resources.FormatInputFormattersAreRequired(
                        typeof(MvcOptions).FullName,
                        nameof(MvcOptions.InputFormatters),
                        typeof(IInputFormatter).FullName));
                }

                return new BodyModelBinder(_formatters, _readerFactory, _loggerFactory, _options);
            }

            return null;
        }
        if (context.BindingInfo.BindingSource != null && context.BindingInfo.BindingSource.CanAcceptDataFrom(BindingSource.Body))

所以默认绑定器变成了BodyModelBinder


特性详解:特性配置后的影响

上一篇下一篇

猜你喜欢

热点阅读