美文网首页asp.net core mvc系列文章
asp.net core mvc action参数绑定 从使用到

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

作者: 李浩的博客 | 来源:发表于2019-03-01 11:01 被阅读0次

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

    Q1: 数据是哪来的?

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

      1. 来自值提供器的数据,值提供器就是继承了接口IValueProvider
        /// <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);
        }
    
      1. 解析器 有些数据不是以键值对表示的数据结构,比如文件,json,xml,类型复杂,必须要解析器才能解析出请求的数据。
        解析器就是实现了IInputFormatter
     /// <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);
        }
    
      1. request.Headers 数据源。
        FromHeader,就是从request.Headers中取数据。
      1. 来自DI的数据
        DI容器中的对象可以直接通过[FromService]特性加载到Action中。

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

      1. 值提供器
      class DecimalModelBinder
    
      public Task BindModelAsync(ModelBindingContext bindingContext)
      {
            var modelName = bindingContext.ModelName;
            var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
      }
    
      1. 解析器
     class BodyModelBinder
    
     public async Task BindModelAsync(ModelBindingContext bindingContext)
            {
    
                    var result = await formatter.ReadAsync(formatterContext);
    
                    if (result.HasError)
                    {
                    }
    
                    if (result.IsModelSet)
                    {
                    }
                    else
                    {
                    }
            }
    
    

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

    • 值提供器和解析器都是绑定器的数据源
    • 值提供器和解析器 与 绑定器的关系是 一对多,也就是一个值提供器和解析器可以为多种绑定器提供数据。

    值提供器


    • CompositeValueProvider
    • FormValueProvider
    • JQueryFormValueProvider
    • JQueryQueryStringValueProvider
    • QueryStringValueProvider
    • RouteValueProvider

    解析器

    • JsonInputFormatter
    • JsonPatchInputFormatter
    • XmlDataContractSerializerInputFormatter
    • XmlSerializerInputFormatter

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

    • ByteArrayModelBinder
    • DecimalModelBinder
    • CollectionModelBinder<TElement>
    • FloatModelBinder
    • DoubleModelBinder
    • SimpleTypeModelBinder

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

    • BodyModelBinder

    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 判断我能不能行。


    • FromBodyAttribute
    • FromFormAttribute
    • FromHeaderAttribute
    • FromQueryAttribute
    • FromRouteAttribute
    • FromServicesAttribute

    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

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

    • ByteArrayModelBinder
    • DecimalModelBinder
    • CollectionModelBinder<TElement>
    • FloatModelBinder
    • DoubleModelBinder
    • SimpleTypeModelBinder
    • ComplexTypeModelBinder

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


    也就是说,默认情况下,绑定的数据来源都是值提供器。而 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))
    
    • 这个判断中的context.BindingInfo.BindingSource的值是读取的FromBody中的值
    • FromBody中的BindingSource 是 BindingSource.Body

    所以默认绑定器变成了BodyModelBinder


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

    • FromBodyAttribute 影响绑定器,设置了FromBody会导致默认绑定器变成了BodyModelBinder
    • FromHeaderAttribute影响绑定器,设置了FromHeader会导致默认绑定器变成了HeaderModelBinder,HeaderModelBinder有个比较特殊的地方在于他不是从解析器和值提供器中获取数据,而是从request.Headers中获取数据。
    • FromFormAttribute 影响值提供器,导致绑定器只能从FormValueProvider JQueryFormValueProvider中获取
    • FromQueryAttribute 影响值提供器,导致绑定器只能从JQueryQueryStringValueProvider QueryStringValueProvider中获取
    • FromRouteAttribute 影响值提供器,导致绑定器只能从RouteValueProvider中获取
    • FromServicesAttribute 内部的获取方式是:HttpContext.RequestServices.GetService(),手动从容器中抓取的对象。

    相关文章

      网友评论

        本文标题:asp.net core mvc action参数绑定 从使用到

        本文链接:https://www.haomeiwen.com/subject/ljqfuqtx.html