【问题标题】:Model property bindings with [FromBody] attribute具有 [FromBody] 属性的模型属性绑定
【发布时间】:2017-09-11 22:04:02
【问题描述】:

我想在原始数据分配给模型属性之前对其进行一些预处理。即用点替换逗号以允许将此字符串“324.32”和“324,32”都转换为双精度。所以我写了这个模型活页夹

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;
        }

    }

然后是适当的提供者

 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;
        }
    }

并在 Startup.cs 中注册它

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

        });

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

 public class Model
    {
        public string Str { get; set; }
        public double Number { get; set; }
    }


    [HttpPost]
    public IActionResult Post(Model model)
    {

        return Ok("ok");
    }

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

【问题讨论】:

  • 无法评论您所描述的问题,但作为一种简单的解决方法,您可以接受视图模型(将这些属性作为字符串),然后使用 AutoMapper 将其映射到您的实体模型/dto在您的控制器操作中。
  • 您在发帖时是否明确设置了内容类型标头?仅仅因为它看起来像 JSON 并不意味着它会被解释为 JSON。

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


【解决方案1】:

我找到了解决方案。正如它所描述的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());
        });

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-24
    • 2018-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-30
    • 1970-01-01
    相关资源
    最近更新 更多