将 JSON 对象传递给 MVC 控制器时 string.empty 转换为 null
Posted
技术标签:
【中文标题】将 JSON 对象传递给 MVC 控制器时 string.empty 转换为 null【英文标题】:string.empty converted to null when passing JSON object to MVC Controller 【发布时间】:2012-09-25 21:23:51 【问题描述】:我正在将一个对象从客户端传递到服务器。在此过程中,表示为 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,因此我不想在服务器上单独检查属性。
【问题讨论】:
尝试对数据进行字符串化:data: JSON.stringify(dataToPost),
,您还应该将 contentType 指定为:contentType: "application/json"
@nemesv 感谢您的建议。应用 JSON.stringify(dataToPost) 并指定内容类型后,数据仍显示为 null。
【参考方案1】:
这是一个将空字符串绑定到null
s 的 MVC 功能。
此逻辑由DefaultModelBinder
使用的ModelMetadata.ConvertEmptyStringToNull 属性控制。
您可以使用DisplayFormat
属性设置ConvertEmptyStringToNull
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();
您可以阅读有关此功能的更多信息here。
【讨论】:
这显然是问题的正确答案。谢谢你。更新:也像宣传的那样工作! 如果我只为一个参数设置了 ModelBinderAttribute 属性,如上所述,上述解决方案不起作用。似乎在绑定单个模型属性时使用了标准 DefaultModelBinder,这使得它们无论如何都为空。我对此的解决方法是将以下代码添加到重写的 BindModel() 方法的开头:Binders = new ModelBinderDictionary() DefaultBinder = this ;
确认:上面的例子在应用属性时不起作用
@Anders 感谢修复。我已经用这些信息更新了我的帖子。
使用 System.ComponentModel.DataAnnotations;【参考方案2】:
与其像一些答案建议的那样创建修改 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
默认返回CachedDataAnnotationsModelMetadataProvider
的实例。 CachedDataAnnotationsModelMetadataProvider
密封 CreateMetaData 方法,因此您不能像您在此处建议的那样覆盖它。我猜缓存的提供程序性能更高,所以我不确定这个答案是否是个好主意,因为它不使用缓存的提供程序(无论如何也不能)。【参考方案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)])。没有人愿意在每个属性上都这样做,因为这很愚蠢。
【讨论】:
我在数组方面遇到了类似的问题。当涉及到 mvc-actions 的参数时,像 " foo: [] " 这样的 Json 字符串会导致 foo=null。有什么方法可以调整默认 mvc json-deserializer 的反序列化,以便它将给定的 json-string 转换为实际的空数组(也就是不为空)?【参考方案4】:问题在于,当字符串为空时,AutoMapper 会将可空值变为空值。另一个问题的答案是我认为可以满足您的需求:Automapper null string to empty
【讨论】:
感谢您的回复。如果您查看我的第二个屏幕截图,您会看到我的监视窗口悬停在传递给 SaveOrderDetails 的参数上。 AutoMapper 还没有发挥作用,但也许 AutoMapper 可以解决这个问题。我会玩它的。【参考方案5】:使用$.ajax
发布数据时,null
不是data
选项属性的可能值。如果您使用 http 调试器查看请求,您会看到它已转换为空字符串。
所以我猜你的 MVC 控制器正在应用相反的转换。
我在 ajax 应用程序中解决此问题的方法是我不使用 $.ajax()
的 data
选项,但我将所有内容序列化为 JSON 并将其放入数据的单个字段“数据”中选项。像这样你没有空值的问题。当然,你必须在服务器端反序列化。
【讨论】:
以上是关于将 JSON 对象传递给 MVC 控制器时 string.empty 转换为 null的主要内容,如果未能解决你的问题,请参考以下文章
Angular2 http post没有将json对象传递给MVC 6控制器动作
如何将 javascript 对象传递给 C# MVC 4 控制器