源码太多,涉及太广,作为使用者其实不用了解那么多,带着问题去看看源码吧。
Q1: 数据是哪来的?
Q1.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);
}
- 解析器 有些数据不是以键值对表示的数据结构,比如文件,json,xml,类型复杂,必须要解析器才能解析出请求的数据。
解析器就是实现了IInputFormatter
- 解析器 有些数据不是以键值对表示的数据结构,比如文件,json,xml,类型复杂,必须要解析器才能解析出请求的数据。
/// <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);
}
- request.Headers 数据源。
FromHeader,就是从request.Headers中取数据。
- request.Headers 数据源。
- 来自DI的数据
DI容器中的对象可以直接通过[FromService]特性加载到Action中。
- 来自DI的数据
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 什么时候用值提供器,什么时候用解析器
- 值提供器和解析器都是绑定器的数据源
- 值提供器和解析器 与 绑定器的关系是 一对多,也就是一个值提供器和解析器可以为多种绑定器提供数据。
值提供器
- 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.png2. 绑定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(),手动从容器中抓取的对象。
网友评论