使用[FromBody]属性建模属性绑定

时间:2017-09-11 10:12:32

标签: c# asp.net-core asp.net-core-mvc

我希望在将原始数据分配给模型属性之前对其应用一些预处理。即用逗号替换逗号以允许转换这两个字符串" 324.32"和" 324,32"成双。所以我写了这个模型绑定器

if(conn!=null){
    conn.close();
}

然后是适当的提供者

public class MoneyModelBinder: IModelBinder
    {
        private readonly Type _modelType;
        public MoneyModelBinder(Type modelType)
        {
            _modelType = modelType;
        }

        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }

            string modelName = bindingContext.ModelName;


            ValueProviderResult providerResult = bindingContext.ValueProvider.GetValue(modelName);

            if (providerResult == ValueProviderResult.None)
            {
                return TaskCache.CompletedTask;
            }

            bindingContext.ModelState.SetModelValue(modelName, providerResult);

            string value = providerResult.FirstValue;

            if (string.IsNullOrEmpty(value))
            {
                return TaskCache.CompletedTask;
            }

            value = value.Replace(",", ".");

            object result;
            if(_modelType == typeof(double))
            {
                result = Convert.ToDouble(value, CultureInfo.InvariantCulture);
            }
            else if(_modelType == typeof(decimal))
            {
                result = Convert.ToDecimal(value, CultureInfo.InvariantCulture);
            }
            else if(_modelType == typeof(float))
            {
                result = Convert.ToSingle(value, CultureInfo.InvariantCulture);
            }
            else
            {
                throw new NotSupportedException($"binder doesn't implement this type {_modelType}");
            }


            bindingContext.Result = ModelBindingResult.Success(result);
            return TaskCache.CompletedTask;
        }

    }

并在Startup.cs中注册

 public class MoneyModelBinderProvider : IModelBinderProvider
    {
        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if(context.Metadata?.ModelType == null)
            {
                return null;
            }


            if (context.Metadata.ModelType.In(typeof(double), typeof(decimal), typeof(float)))
            {
                return new MoneyModelBinder(context.Metadata.ModelType);
            }
            return null;
        }
    }

但我注意到一些奇怪的行为或者我错过了一些东西。如果我使用这种行动

services.AddMvc(options =>
        {
            options.ModelBinderProviders.Insert(0, new MoneyModelBinderProvider());

        });

并在查询字符串中提供参数一切正常:首先为模型本身调用提供者,然后为模型的每个属性调用。 但是如果我使用[FromBody]属性并通过JSON提供参数,则会为模型调用provider,但从不调用此模型的属性。但为什么?如何在FromBody中使用活页夹?

1 个答案:

答案 0 :(得分:0)

我找到了解决方案。正如其描述的here [FromBody]在与其他值提供者的比较中表现不同 - 它通过JsonFormatters一次性转换复杂对象。因此,除了模型绑定器,我们还应该为FromBody编写单独的逻辑。当然,我们可以在json处理期间获得一些观点:

public class MoneyJsonConverter : JsonConverter
{
    public override bool CanWrite => false;

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(double);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        string value = (reader.Value ?? "").Replace(" ", "").Replace(",", ".");

        TypeConverter converter = TypeDescriptor.GetConverter(modelType);
        object result = converter.ConvertFromInvariantString(value);

        return result;;   
    }
}

并使用

        services.AddMvc(options =>
        {
            options.ModelBinderProviders.Insert(0, new MoneyModelBinderProvider());

        }).AddJsonOptions(options =>
        {              
            options.SerializerSettings.Converters.Add(new MoneyJsonConverter());
        });