Rob*_*III 6 c# model-binding asp.net-web-api asp.net-core asp.net-core-webapi
我只是浪费了很多时间来尝试定制ComplexTypeModelBinder工作.无论我做了什么,它都没有奏效.事实证明,这仅在数据作为表单数据发布时有效; 当你发布一个JSON对象(在我的情况下从Swagger"试用"表单)时,ComplexTypeModelBinder永远不会调用该SetProperty方法.
我有很多模型,有些比其他更复杂,我用自定义属性注释了一些属性.每当该属性被绑定时,我希望它" 标准化 "(对它应用一些"格式化"),以便在模型得到验证时,验证器可以看到"标准化"数据而不是用户输入的数据.
我真的,真的,想要保持当前的模型绑定行为,因为它目前工作正常但有一个例外,即注释属性由我实现的一些代码处理.所有其他属性和行为应保持原样.这就是我希望继承的原因ComplexTypeModelBinder,但是,事实证明,如果数据以JSON的形式发布,则这不起作用.
我的(示例)模型看起来像:
public class MyComplexModel
{
public int Id { get; set; }
public string Name { get; set; }
[FormatNumber(NumberFormat.E164)]
public string PhoneNumber { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我的控制器方法如下所示:
[HttpPost]
public MyComplexModel Post(MyComplexModel model)
{
return model;
}
Run Code Online (Sandbox Code Playgroud)
我的(不工作)自定义ComplexTypeModelBinder看起来像:
public class MyModelBinder : ComplexTypeModelBinder
{
private readonly INumberFormatter _numberformatter;
private static readonly ConcurrentDictionary<Type, Dictionary<string, FormatNumberAttribute>> _formatproperties = new ConcurrentDictionary<Type, Dictionary<string, FormatNumberAttribute>>();
public MyModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders, INumberFormatter numberFormatter, ILoggerFactory loggerFactory)
: base(propertyBinders, loggerFactory)
{
_numberformatter = numberFormatter;
}
protected override object CreateModel(ModelBindingContext bindingContext)
{
// Index and cache all properties having the FormatNumber Attribute
_formatproperties.GetOrAdd(bindingContext.ModelType, (t) =>
{
return t.GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(FormatNumberAttribute))).ToDictionary(pi => pi.Name, pi => pi.GetCustomAttribute<FormatNumberAttribute>(), StringComparer.OrdinalIgnoreCase);
});
return base.CreateModel(bindingContext);
}
protected override Task BindProperty(ModelBindingContext bindingContext)
{
return base.BindProperty(bindingContext);
}
protected override void SetProperty(ModelBindingContext bindingContext, string modelName, ModelMetadata propertyMetadata, ModelBindingResult result)
{
if (_formatproperties.TryGetValue(bindingContext.ModelType, out var props) && props.TryGetValue(modelName, out var att))
{
// Do our formatting here
var formatted = _numberformatter.FormatNumber(result.Model as string, att.NumberFormat);
base.SetProperty(bindingContext, modelName, propertyMetadata, ModelBindingResult.Success(formatted));
} else
{
// Do nothing
base.SetProperty(bindingContext, modelName, propertyMetadata, result);
}
}
}
Run Code Online (Sandbox Code Playgroud)
(MyModelBinder对FormatNumber属性的实际检查并相应地处理属性,但为了简洁起见,我将其留下来,因为它并不重要).
我的ModelBinderProvider:
public class MyModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
var modelType = context.Metadata.ModelType;
if (!typeof(MyComplexModel).IsAssignableFrom(modelType))
return null;
if (!context.Metadata.IsComplexType || context.Metadata.IsCollectionType)
return null;
var propertyBinders = context.Metadata.Properties
.ToDictionary(modelProperty => modelProperty, context.CreateBinder);
return new MyModelBinder(
propertyBinders,
(INumberFormatter)context.Services.GetService(typeof(INumberFormatter)),
(ILoggerFactory)context.Services.GetService(typeof(ILoggerFactory))
);
}
}
Run Code Online (Sandbox Code Playgroud)
当然,我在StartUp课堂上添加了提供者:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(config =>
{
config.ModelBinderProviders.Insert(0, new MyModelBinderProvider());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
Run Code Online (Sandbox Code Playgroud)
同样,当数据作为表单数据发布而不是作为JSON发布时,这可以正常工作.实现这个的正确方法是什么?我已经读过某个地方,我不应该ModelBinding朝着方向看,而是在"JSON转换器"方向,但我还没有找到任何实际工作的东西(还).
编辑:我已经创建了一个Git仓库来证明我的问题在这里.要看到我的问题,设置一个断点在这里在TestController这里的模型在返回的Post方法.启动项目; 一个简单的网页将显示两个按钮.左边的一个会将表单数据发布为表单数据,您将看到返回的模型带有反向的语音(例如).单击右侧按钮,数据将作为JSON模型发布.请注意,返回的模型具有0个id和null两个属性的值.
| 归档时间: |
|
| 查看次数: |
1090 次 |
| 最近记录: |