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

IT技术 c# javascript jquery asp.net-mvc
2021-03-09 21:42:23

我正在将一个对象从客户端传递到服务器。在此过程中,表示为 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 对象。我无法在服务器上将所有空值强制为 string.empty。

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

5个回答

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

此逻辑由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();

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

确认:上面的示例在操作中应用属性不起作用
2021-04-26 21:42:23
这显然是问题的正确答案。谢谢你。更新:也像宣传的那样工作!
2021-04-29 21:42:23
如果我只为单个参数设置 ModelBinderAttribute 属性,则上述解决方案不起作用。绑定单个模型属性时似乎使用了标准的 DefaultModelBinder,这使得它们无论如何都为空。我对此的解决方法是将以下代码添加到重写的 BindModel() 方法的开头:Binders = new ModelBinderDictionary() { DefaultBinder = this };
2021-04-29 21:42:23
使用 System.ComponentModel.DataAnnotations;
2021-05-05 21:42:23
@Anders 感谢您的修复。我已经用信息更新了我的帖子。
2021-05-07 21:42:23

不是像一些答案建议的那样创建一个修改 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();
在 MVC 5.2.3 中,默认ModelMetadataProviders.Current返回 的实例CachedDataAnnotationsModelMetadataProviderCachedDataAnnotationsModelMetadataProvider密封CreateMetaData方法让喜欢你的建议在这里你不能覆盖它。我猜缓存提供程序的性能更高,所以我不确定这个答案是否是个好主意,因为它不使用缓存提供程序(无论如何也不能)。
2021-05-10 21:42:23

接受的答案对我使用 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)])。没有人想在每个属性上都这样做,因为这很愚蠢。

我面临着与数组类似的问题。当涉及到 mvc-actions 的参数时,像 "{ foo: [] }" 这样的 Json 字符串会导致 foo=null。有什么方法可以调整默认 mvc json-deserializer 的反序列化,以便将给定的 json-string 转换为实际的空数组(又名非空)?
2021-05-17 21:42:23

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

感谢您的答复。如果您查看我的第二个屏幕截图,您会看到我的监视窗口悬停在传递给 SaveOrderDetails 的参数上。AutoMapper 还没有发挥作用,但也许 AutoMapper 可以解决这个问题。我会玩它。
2021-04-20 21:42:23

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

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

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