将自定义模型绑定器应用于 asp.net 核心中的对象属性
Posted
技术标签:
【中文标题】将自定义模型绑定器应用于 asp.net 核心中的对象属性【英文标题】:Apply Custom Model Binder to Object Property in asp.net core 【发布时间】:2019-07-06 18:59:43 【问题描述】:我正在尝试为模型的 DateTime 类型属性应用自定义模型绑定器。 这是 IModelBinder 和 IModelBinderProvider 的实现。
public class DateTimeModelBinderProvider : IModelBinderProvider
public IModelBinder GetBinder(ModelBinderProviderContext context)
if (context == null)
throw new ArgumentNullException(nameof(context));
if (context.Metadata.ModelType == typeof(DateTime))
return new BinderTypeModelBinder(typeof(DateTime));
return null;
public class DateTimeModelBinder : IModelBinder
private string[] _formats = new string[] "yyyyMMdd", "yyyy-MM-dd", "yyyy/MM/dd"
, "yyyyMMddHHmm", "yyyy-MM-dd HH:mm", "yyyy/MM/dd HH:mm"
, "yyyyMMddHHmmss", "yyyy-MM-dd HH:mm:ss", "yyyy/MM/dd HH:mm:ss";
private readonly IModelBinder baseBinder;
public DateTimeModelBinder()
baseBinder = new SimpleTypeModelBinder(typeof(DateTime), null);
public Task BindModelAsync(ModelBindingContext bindingContext)
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult != ValueProviderResult.None)
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
var value = valueProviderResult.FirstValue;
if (DateTime.TryParseExact(value, _formats, new CultureInfo("en-US"), DateTimeStyles.None, out DateTime dateTime))
bindingContext.Result = ModelBindingResult.Success(dateTime);
else
bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, $"bindingContext property value format error.");
return Task.CompletedTask;
return baseBinder.BindModelAsync(bindingContext);
这里是模型类
public class Time
[ModelBinder(BinderType = typeof(DateTimeModelBinder))]
public DateTime? validFrom get; set;
[ModelBinder(BinderType = typeof(DateTimeModelBinder))]
public DateTime? validTo get; set;
这里是控制器动作方法。
[HttpPost("/test")]
public IActionResult test([FromBody]Time time)
return Ok(time);
测试时,不会调用自定义活页夹,但会调用默认的 dotnet 活页夹。据官方documentation称,
ModelBinder 属性可以应用于单个模型属性 (例如在视图模型上)或以操作方法参数指定一个 仅用于该类型或操作的特定模型绑定器或模型名称。
但它似乎不适用于我的代码。
【问题讨论】:
【参考方案1】:1.原因
根据您操作中的[FromBody]Time time
,我猜您正在发送带有Content-Type
或application/json
的有效负载。在这种情况下,当接收到 josn 有效负载时,模型绑定系统将检查参数time
,然后尝试为其找到合适的绑定器。因为context.Metadata.ModelType
等于typeof(Time)
而不是typeof(DateTime)
,并且typeof(Time)
没有自定义ModelBinder,所以您的GetBinder(context)
方法将返回null
:
public class DateTimeModelBinderProvider : IModelBinderProvider
public IModelBinder GetBinder(ModelBinderProviderContext context)
if (context == null)
throw new ArgumentNullException(nameof(context));
if (context.Metadata.ModelType == typeof(DateTime)) // not typeof(Time)
return new BinderTypeModelBinder(typeof(DateTime));
return null;
因此它回退到 application/json 的默认模型绑定器。默认的 json 模型绑定器在后台使用Newtonsoft.Json
,并将简单地将洞有效负载反序列化为Time
的实例。因此,您的DateTimeModelBinder
不会被调用。
2。快速修复
一种方法是使用application/x-www-form-urlencoded
(避免使用application/json
)
移除[FromBody]
属性:
[HttpPost("/test2")]
public IActionResult test2(Time time)
return Ok(time);
并以application/x-www-form-urlencoded
的格式发送payload
POST https://localhost:5001/test2
Content-Type: application/x-www-form-urlencoded
validFrom=2018-01-01&validTo=2018-02-02
它现在应该可以工作了。
3。使用 JSON
如下创建一个自定义转换器:
public class CustomDateConverter : JsonConverter
public override bool CanConvert(Type objectType)
return true;
public static string[] _formats = new string[]
"yyyyMMdd", "yyyy-MM-dd", "yyyy/MM/dd"
, "yyyyMMddHHmm", "yyyy-MM-dd HH:mm", "yyyy/MM/dd HH:mm"
, "yyyyMMddHHmmss", "yyyy-MM-dd HH:mm:ss", "yyyy/MM/dd HH:mm:ss"
;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
var dt= reader.Value;
if (DateTime.TryParseExact(dt as string, _formats, new CultureInfo("en-US"), DateTimeStyles.None, out DateTime dateTime))
return dateTime;
else
return null;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
serializer.Serialize(writer, value as string);
我只是复制您的代码以格式化日期。
如下更改您的模型:
public class Time
[ModelBinder(BinderType = typeof(DateTimeModelBinder))]
[JsonConverter(typeof(CustomDateConverter))]
public DateTime? validFrom get; set;
[ModelBinder(BinderType = typeof(DateTimeModelBinder))]
[JsonConverter(typeof(CustomDateConverter))]
public DateTime? validTo get; set;
现在您可以使用[FromBody]
接收时间
[HttpPost("/test")]
public IActionResult test([FromBody]Time time)
return Ok(time);
【讨论】:
以上是关于将自定义模型绑定器应用于 asp.net 核心中的对象属性的主要内容,如果未能解决你的问题,请参考以下文章
为啥 ASP.Net MVC 模型绑定器将空 JSON 数组绑定到 null?
Asp.Net Core Razor Page PUT Handler 模型绑定器未绑定来自 ajax put 上的 java 脚本的序列化表单