String.empty преобразуется в null при передаче объекта JSON в MVC Controller

Я передаю объект от клиента к серверу. Свойства объекта, которые представлены как string.empty, во время этого процесса преобразуются в нуль. Мне было интересно, как предотвратить это, когда тип объектов поддерживает string.empty.

enter image description here

console.log("DataToPost:", dataToPost);

$.ajax({
    type: "POST",
    contentType: 'application/json'
    url: "../../csweb/Orders/SaveOrderDetails/",
    data: dataToPost,
    success: function (result) {
        console.log(result);
    },
    error: function (e) {
        console.error(e);
    }
});

enter image description here

Моя модель включает в себя объекты DateTime с нулевым значением. Я не могу заставить все нули string.empty на сервере.

Я использую AutoMapper, поэтому я бы предпочел не проверять свойства на сервере.

Ответ 1

Это функция MVC, которая связывает пустые строки с null s.

Эта логика управляется с помощью свойства ModelMetadata.ConvertEmptyStringToNull, которое используется DefaultModelBinder.

Вы можете установить ConvertEmptyStringToNull с помощью атрибута DisplayFormat

public class OrderDetailsModel
{
    [DisplayFormat(ConvertEmptyStringToNull = false)]
    public string Comment { get; set; }

    //...
}

Однако, если вы не хотите комментировать все свойства, вы можете создать настраиваемое связующее устройство, где вы установите его на false:

public class EmptyStringModelBinder : DefaultModelBinder 
{
    public override object BindModel(ControllerContext controllerContext,
                                     ModelBindingContext bindingContext)
    {
        bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
        Binders = new ModelBinderDictionary() { DefaultBinder = this };
        return base.BindModel(controllerContext, bindingContext);
    }
}

И вы можете использовать ModelBinderAttribute в своем действии:

public ActionResult SaveOrderDetails([ModelBinder(typeof(EmptyStringModelBinder))] 
       OrderDetailsModel orderDetailsModel)
{
}

Или вы можете установить его как глобальный ModelBinder по умолчанию в свой Global.asax:

ModelBinders.Binders.DefaultBinder = new EmptyStringModelBinder();

Подробнее об этой функции здесь.

Ответ 2

Вместо создания ModelBinder, который модифицирует ModelMetadata в качестве некоторых предложенных ответов, более простой альтернативой является предоставление пользовательского ModelMetadataProvider.

public class EmptyStringDataAnnotationsModelMetadataProvider : System.Web.Mvc.DataAnnotationsModelMetadataProvider 
{
    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        var modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
        modelMetadata.ConvertEmptyStringToNull = false;
        return modelMetadata;
    }
}

Затем в Application_Start()

ModelMetadataProviders.Current = new EmptyStringDataAnnotationsModelMetadataProvider();

Ответ 3

Принятый ответ не работал у меня с использованием MVC4. Тем не менее, следующий обходной путь делает, и я думал, что это поможет другим.

public class CustomModelBinder : DefaultModelBinder
{
    public bool ConvertEmptyStringToNull { get; set; }

    public CustomModelBinder ()
    {
    }

    public CustomModelBinder (bool convertEmptyStringToNull)
    {
        this.ConvertEmptyStringToNull = convertEmptyStringToNull;
    }

    protected override bool OnModelUpdating(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // this little bit is required to override the ConvertEmptyStringToNull functionality that we do not want!

        foreach (string propertyKey in bindingContext.PropertyMetadata.Keys)
        {
            if(bindingContext.PropertyMetadata[propertyKey] != null)
                    bindingContext.PropertyMetadata[propertyKey].ConvertEmptyStringToNull = this.ConvertEmptyStringToNull;
        }
        return base.OnModelUpdating(controllerContext, bindingContext);
    }


}

Это устранит проблему в MVC4+. Казалось бы, bindingContext.ModelMetadata.ConvertEmptyStringToNull полностью игнорируется, и это связано с тем, что этот параметр существует в объекте PropertyMetadata для каждого связанного свойства. PropertyMetadata воссоздается в BindProperty(), поэтому, если вы установите его перед вызовом этого метода, он будет перезаписан, если он не существует как атрибут свойства связанного объекта (например, [DisplayFormat (ConvertEmptyStringToNull = false)]). Никто не хочет делать это на каждом свойстве как на глупость.

Ответ 4

Проблема заключается в том, что AutoMapper превращает nullables в null, когда строка пуста. На этот другой вопрос ответил то, что, по моему мнению, удовлетворяет вашим потребностям: Автоматическая пустая строка для пустых

Ответ 5

При отправке данных с $.ajax, null не является возможным значением для свойства параметра data. Если вы посмотрите на запрос с помощью HTTP-отладчика, вы увидите, что он преобразован в пустую строку.

Поэтому я предполагаю, что ваш MVC-контроллер применяет противоположное преобразование.

Что я делаю в приложении ajax для решения этой проблемы, так это то, что я не использую параметр data $.ajax(), но я сериализую все в JSON и помещаю его в одно поле "данные" данных вариант. Например, у вас нет проблем с нулевыми значениями. Конечно, вы должны десериализоваться на стороне сервера.