Asp.net web Api源码分析-ParameterBindingAttribute
在前面数据绑定中我们曾提到一个FromUriAttribute类,它 集成于ModelBinderAttribute,而ModelBinderAttribute又继承 ParameterBindingAttribute,ParameterBindingAttribute类就一个空方法没什么实现,这里主要的实现在 ModelBinderAttribute类里面,其主要方法是GetBinding,
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
HttpConfiguration config = parameter.Configuration;
IModelBinder binder = GetModelBinder(config, parameter.ParameterType);
IEnumerable<ValueProviderFactory> valueProviderFactories = GetValueProviderFactories(config);
return new ModelBinderParameterBinding(parameter, binder, valueProviderFactories);
}
这里的GetModelBinder方法具体实现如下,
public IModelBinder GetModelBinder(HttpConfiguration configuration, Type modelType)
{
if (BinderType == null)
{
ModelBinderProvider provider = GetModelBinderProvider(configuration);
return provider.GetBinder(configuration, modelType);
}
// This may create a IModelBinder or a ModelBinderProvider
object value = GetOrInstantiate(configuration, BinderType);
Contract.Assert(value != null); // Activator would have thrown
IModelBinder binder = value as IModelBinder;
if (binder != null)
{
return binder;
}
else
{
ModelBinderProvider provider = value as ModelBinderProvider;
if (provider != null)
{
return provider.GetBinder(configuration, modelType);
}
}
Type required = typeof(IModelBinder);
throw
Error.InvalidOperation(SRResources.ValueProviderFactory_Cannot_Create,
required.Name, value.GetType().Name, required.Name);
}
这里的检查BinderType 是否为null,这里首先调用GetModelBinderProvider来获取一个ModelBinderProvider实例,
public ModelBinderProvider GetModelBinderProvider(HttpConfiguration configuration)
{
if (BinderType != null)
{
object value = GetOrInstantiate(configuration, BinderType);
if (value != null)
{
VerifyBinderType(value.GetType());
ModelBinderProvider result = (ModelBinderProvider)value;
return result;
}
}
// Create default over config
IEnumerable<ModelBinderProvider> providers = configuration.Services.GetModelBinderProviders();
if (providers.Count() == 1)
{
return providers.First();
}
return new CompositeModelBinderProvider(providers);
},
这你的GetOrInstantiate方法非常简单,根据BinderType直接创建一个实例,创建方法configuration.DependencyResolver.GetService,然后验证实例是否是ModelBinderProvider类型实例,如果不是抛出异常,如果是则直接转化为ModelBinderProvider并返回。
所以这里我们默认调用configuration.Services.GetModelBinderProviders()来获取providers ,
SetMultiple<ModelBinderProvider>(new TypeConverterModelBinderProvider(),
new TypeMatchModelBinderProvider(),
new KeyValuePairModelBinderProvider(),
new ComplexModelDtoModelBinderProvider(),
new ArrayModelBinderProvider(),
new DictionaryModelBinderProvider(),
new CollectionModelBinderProvider(),
new MutableObjectModelBinderProvider());
所以我们默认的providers 就有这8个,它们每个对应着自己的IModelBinder
TypeConverterModelBinderProvider->TypeConverterModelBinder
TypeMatchModelBinderProvider->TypeMatchModelBinder
KeyValuePairModelBinderProvider->KeyValuePairModelBinder<,>
ComplexModelDtoModelBinderProvider->CompositeModelBinder
ArrayModelBinderProvider-> ArrayModelBinder<T>
DictionaryModelBinderProvider->DictionaryModelBinder<,>
CollectionModelBinderProvider->CollectionModelBinder<>
MutableObjectModelBinderProvider->MutableObjectModelBinder
如果在调用GetModelBinderProvider方法时,BinderType 值为null时返回的是一个CompositeModelBinderProvider,如果不为null则返回指定类型的ModelBinderProvider。
回到GetModelBinder方法中来,我们先看看BinderType 值为null的情况下是如何执行的,调用CompositeModelBinderProvider的GetBinder方法。
其中CompositeModelBinderProvider的GetBinder方法实现如下:
public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType)
{
IEnumerable<ModelBinderProvider> providers = _providers ?? configuration.Services.GetModelBinderProviders();
IEnumerable<IModelBinder> binders = from provider in providers
let binder = provider.GetBinder(configuration, modelType)
where binder != null
select binder;
return new CompositeModelBinder(binders);
}
从当前的providers中依次调用他们的GetBinder方法,然后利用返回的IModelBinder集合创建一个新的CompositeModelBinder实例。
现在我们回到GetModelBinder方法中来,BinderType 为null的情况下返回一个CompositeModelBinder实例,不为null就首先根据BinderType 来创建新的实例,如果它是IModelBinder就直接返回,如果它是ModelBinderProvider就调用它的GetBinder方法并返回,否者抛出异常。
现在回到GetBinding方法中来,IModelBinder已经取到了,接下来是如何获取ValueProviderFactory集合。
public virtual IEnumerable<ValueProviderFactory>
GetValueProviderFactories(HttpConfiguration configuration)
{
// By default, just get all registered value provider factories
return configuration.Services.GetValueProviderFactories();
}
SetMultiple<ValueProviderFactory>(new QueryStringValueProviderFactory(),
new RouteDataValueProviderFactory());
其中FromUriAttribute重写父类的GetValueProviderFactories方法,
public override IEnumerable<ValueProviderFactory> GetValueProviderFactories(HttpConfiguration configuration)
{
foreach (ValueProviderFactory f in base.GetValueProviderFactories(configuration))
{
if (f is IUriValueProviderFactory)
{
yield return f;
}
}
}
默认的QueryStringValueProviderFactory、RouteDataValueProviderFactory都实现了IUriValueProviderFactory接口,而CompositeValueProviderFactory就没有实现它。
到这里ModelBinderAttribute中的GetBinding方法最后就只剩下ModelBinderParameterBinding的实例化了,这个比较简单忽略它了。
在DefaultActionValueBinder有这么一句new FromBodyAttribute().GetBinding(parameter),首先看看FromBodyAttribute的实现吧:
public sealed class FromBodyAttribute : ParameterBindingAttribute
{
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
IEnumerable<MediaTypeFormatter> formatters = parameter.Configuration.Formatters;
IBodyModelValidator validator = parameter.Configuration.Services.GetBodyModelValidator();
return parameter.BindWithFormatter(formatters, validator);
}
}
SetSingle<IBodyModelValidator>(new DefaultBodyModelValidator());这里的validator 其实是一个DefaultBodyModelValidator实例。
其中BindWithFormatter的方法实现如下:
public static HttpParameterBinding BindWithFormatter(this
HttpParameterDescriptor parameter, IEnumerable<MediaTypeFormatter>
formatters, IBodyModelValidator bodyModelValidator)
{
return new FormatterParameterBinding(parameter, formatters, bodyModelValidator);
}
这里返回一个FormatterParameterBinding实例。所以默认情况下我们主要还用的ModelBinderParameterBinding、FormatterParameterBinding它们2个。后面再绑定参数的时候就是调用他们的ExecuteBindingAsync方法。
总结一下吧:web api的参数绑定和mvc参数绑定差不多,它主要是从路由、url、form来获取参数,路由和url里面的参数结果相对简单,然而from里面的参数有时候需要一个format来格式化,在实际应用中典型的是josn的格式化,返回也需要一个格式化的。在开发Action时建议参数加上Attritute比较好,这样就节省DefaultActionValueBinder.GetParameterBinding方法的开销,他直接返回parameter.ParameterBinderAttribute,而不用像 parameter.Configuration.ParameterBindingRules、parameter.BindWithAttribute(new FromUriAttribute())、new FromBodyAttribute().GetBinding(parameter)这几个复杂而又麻烦的方式获取HttpParameterBinding实例,代码如:
public void Post([FromBody]string value)
{
}
同是web api里面的IModelBinder这里主要有8个实现(TypeConverterModelBinder TypeMatchModelBinder KeyValuePairModelBinder<,> CompositeModelBinder ArrayModelBinder<T> DictionaryModelBinder<,> CollectionModelBinder<> MutableObjectModelBinder),而mvc则只有一个DefaultModelBinder实现。同样web api中的ValueProviderFactory实现也比较简单就默认只有CompositeValueProviderFactory、QueryStringValueProviderFactory、RouteDataValueProviderFactory。总之我们知道web api主要应用就是Rest,它是相对mvc来说是一个轻量级handler,比我们普通的httphandler功能要强大很多,典型是路由和数据格式化的支持。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构