c# - .Net Core 2.2 Web API 中的模型绑定 JSON
问题描述
我只是浪费了很多时间试图让一个习惯ComplexTypeModelBinder
起作用。无论我做什么,它都没有奏效。事实证明,这仅在数据作为表单数据 POST 时才有效;当您发布 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; }
}
我的控制器方法如下所示:
[HttpPost]
public MyComplexModel Post(MyComplexModel model)
{
return model;
}
我的(不工作)自定义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);
}
}
}
(实际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))
);
}
}
当然,我在StartUp
课堂上添加了提供者:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(config =>
{
config.ModelBinderProviders.Insert(0, new MyModelBinderProvider());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
同样,当数据作为表单数据发布时,这可以正常工作,但在作为 JSON 发布时则不行。实现这一点的正确方法是什么?我在某处读到我不应该朝这个ModelBinding
方向看,而是朝“JSON 转换器”方向看,但我还没有找到任何实际工作的东西(还)。
编辑:我创建了一个 git 存储库来在这里演示我的问题。要查看我的问题,请在方法中返回模型的位置设置断点。启动项目;将显示一个带有两个按钮的简单网页。左边的将表单数据作为表单数据发布,您将看到返回的模型带有反转的电话号码(作为示例)。单击右键,数据将作为 JSON 模型发布。请注意,返回的模型具有 0 id 和两个属性的值。TestController
Post
null
解决方案
推荐阅读
- python - 在 Flask 中的视图函数之间传递对象
- eclipse - 为什么“访问限制”错误不会出现在 Eclipse 的问题视图中?
- android - Android Studio 无法识别我的 LG5 设备
- javascript - 如何在javascript/react native/redux中实现搜索逻辑
- ionic-framework - 离子原生条形码扫描仪在 Android 中不起作用
- android - Dagger - 依赖注入
- css - 显示网格中可独立滚动的部分
- unit-testing - JHipster 默认测试用例失败
- python - Pandas DataFrame 中的拆分列(定界);列长度与键错误相同
- javascript - 如何删除父元素并保留其子元素而不创建空跨度