35

我正在将一个对象从客户端传递到服务器。在此过程中,表示为 string.empty 的对象的属性将被转换为 null。我想知道当对象类型支持 string.empty 时如何防止这种情况。

在此处输入图像描述

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

在此处输入图像描述

我的模型包括可为空的 DateTime 对象。我不能在服务器上强制所有 null 为 string.empty。

我正在使用 AutoMapper,所以我不想在服务器上单独检查属性。

4

5 回答 5

75

这是一个将空字符串绑定到nulls 的 MVC 特性。

此逻辑由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)
{
}

或者您可以在 Global.asax 中将其设置为全局默认 ModelBinder:

ModelBinders.Binders.DefaultBinder = new EmptyStringModelBinder();

您可以在此处阅读有关此功能的更多信息。

于 2012-10-04T19:21:32.117 回答
10

与其像一些答案所建议的那样创建修改 ModelMetadata 的 ModelBinder,更简洁的替代方法是提供自定义 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();
于 2013-12-01T14:39:48.713 回答
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)])。没有人愿意在每处房产上都这样做,因为那很愚蠢。

于 2013-10-10T18:29:52.303 回答
0

问题是当字符串为空时,AutoMapper 将可空值变为空值。另一个问题的答案是我认为可以满足您的需求:Automapper null string to empty

于 2012-10-04T19:16:45.677 回答
0

使用 发布数据时$.ajaxnull不是data选项属性的可能值。如果您使用 http 调试器查看请求,您会看到它已转换为空字符串。

所以我猜你的 MVC 控制器正在应用相反的转换。

我在 ajax 应用程序中解决此问题的方法是我不使用data选项$.ajax(),但我将 JSON 中的所有内容序列化并将其放入数据选项的单个字段“数据”中。像这样你没有空值的问题。当然,你必须在服务器端反序列化。

于 2012-10-04T19:21:27.660 回答