【问题标题】:Apply Custom Model Binder to Object Property in asp.net core将自定义模型绑定器应用于 asp.net 核心中的对象属性
【发布时间】: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 属性可以应用于单个模型属性 (例如在视图模型上)或以操作方法参数指定一个 仅用于该类型或操作的特定模型绑定器或模型名称。

但它似乎不适用于我的代码。

【问题讨论】:

    标签: c# asp.net-core model-binding


    【解决方案1】:

    1.原因

    根据您操作中的[FromBody]Time time,我猜您正在发送带有Content-Typeapplication/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);
        }
    

    【讨论】:

      猜你喜欢
      • 2019-11-14
      • 1970-01-01
      • 2022-12-16
      • 2017-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多