C# 将JSON对象传递给MVC控制器时,string.empty转换为null

C# 将JSON对象传递给MVC控制器时,string.empty转换为null,c#,javascript,jquery,asp.net-mvc,C#,Javascript,Jquery,Asp.net Mvc,我正在将一个对象从客户端传递到服务器。在此过程中,表示为string.empty的对象属性将转换为null。我想知道当对象类型支持string.empty时如何防止这种情况 console.log("DataToPost:", dataToPost); $.ajax({ type: "POST", contentType: 'application/json' url: "../../csweb/Orders/SaveOrderDetails/", data:

我正在将一个对象从客户端传递到服务器。在此过程中,表示为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,因此我不希望必须单独检查服务器上的属性。

问题是,当字符串为空时,AutoMapper会将nullables变为null。另一个问题得到了我认为可以满足您需要的回答:

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

因此,我猜您的MVC控制器正在应用相反的转换


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

这是一个MVC特性,它将空字符串绑定到
null
s

此逻辑由
DefaultModelBinder
使用的属性控制

您可以使用
DisplayFormat
属性设置
convertEmptyStringToFull

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);
    }
}
您可以在行动中使用:

public ActionResult SaveOrderDetails([ModelBinder(typeof(EmptyStringModelBinder))] 
       OrderDetailsModel orderDetailsModel)
{
}
或者,您可以在Global.asax中将其全局设置为默认ModelBinder:

ModelBinders.Binders.DefaultBinder = new EmptyStringModelBinder();

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

接受的答案对我使用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.ConvertEmptyStringToFull似乎被完全忽略,这是因为每个绑定属性的PropertyMetadata对象中都存在该设置。PropertyMetadata是在BindProperty()中重新创建的,因此如果在方法调用之前设置它,它将被覆盖,除非它作为绑定对象的属性存在(例如[DisplayFormat(ConvertEmptyStringToFull=false)]。没有人希望在每个属性上都这样做,因为这很愚蠢。

与其像一些答案所建议的那样创建修改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;
    }
}

尝试对数据进行字符串化:
data:JSON.stringify(dataToPost),
并且您还应该将contentType指定为:
contentType:“application/JSON”
@nemesv感谢您的建议。应用JSON.stringify(dataToPost)并指定内容类型后,数据仍然显示为null。感谢您的回复。如果您查看我的第二个屏幕截图,您将看到我的监视窗口悬停在传递到SaveOrderDetails的参数上。AutoMapper还没有投入使用,但也许AutoMapper可以解决这个问题。我要玩一玩。这显然是这个问题的正确答案。非常感谢。更新:工程作为广告,太!如果如前所述仅为单个参数设置ModelBinderAttribute属性,则上述解决方案不起作用。似乎在绑定单个模型属性时使用了标准的DefaultModelBinder,这使得它们仍然为空。我对此的修复方法是将以下代码添加到重写的BindModel()方法的开头:
Binders=newmodelbinderdictionary(){DefaultBinder=this}确认:上面的示例无法在中应用属性action@Anders谢谢你的修复。我已经用信息更新了我的帖子。使用System.ComponentModel.DataAnnotations;我在阵列方面也面临类似的问题。当涉及到mvc操作的参数时,像“{foo:[]}”这样的Json字符串会导致foo=null。有没有办法调整默认mvc json反序列化程序的反序列化,使其将给定的json字符串转换为实际的空数组(也称为NOTNULL)?在mvc 5.2.3中,
ModelMetadataProviders.Current
默认返回
CachedDataAnnotationsModelMetadataProvider
的实例。
CachedDataAnnotationsModelMetadataProvider
密封CreateMetaData方法,因此您不能像这里建议的那样覆盖它。我猜缓存提供程序的性能更好,所以我不确定这个答案是否是个好主意,因为它不使用缓存提供程序(而且无论如何也不能)。
ModelMetadataProviders.Current = new EmptyStringDataAnnotationsModelMetadataProvider();