Ajax 传递空值但控制器在 ASP.NET MVC 中为空

Ngu*_*ong 14 c# ajax asp.net-mvc jquery

我正在处理ASP.NET MVC发送Ajax到我的控制器的值,但遇到了问题。

假设我有SampleViewModel这样的:

public class SampleViewModel
{
    private string _firstName = string.Empty;

    public SampleViewModel()
    {
        _firstName = string.Empty;
    }

    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value ?? string.Empty; }
    }

    public string LastName { get; set; }

    public string FullName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

控制器

[HttpPost]
public JsonResult ActionSubmit(SampleViewModel model)
{               
    var result = "";

    if(model.FirstName == null)
          result += "\nFirstName is null";

    if(model.LastName == null)
          result += "\nLastName is null";

    return Json(result);
}
Run Code Online (Sandbox Code Playgroud)

阿贾克斯

$('.submit').click(function() {
        $.ajax({
            url: '@Url.RouteUrl(new{ action="ActionSubmit", controller="Home"})',
            data: JSON.stringify({ FirstName: '', LastName: '', FullName: 'Phong_Nguyen' }),
                  // Even though I use { FirstName: '', LastName: '', FullName: 'Phong_Nguyen' } without JSON.stringify
            type: 'POST',
            dataType: 'json',
            contentType: "application/json; charset=utf-8",
            success: function(resp) {
                   alert(resp);
            }});
         });
Run Code Online (Sandbox Code Playgroud)

如您所见,我发送了空值,但在控制器的一端,我得到了空值(响应值始终为“LastName 为空”): 在此处输入图片说明

  1. 为什么当我在 Ajax 中发送时empty,我null在我的控制器中获得了价值?

  2. 有没有更好的方法和更优雅的方式来解决我的问题,如下所示?

public string FirstName
{
   get { return _firstName; }
   set { _firstName = value ?? string.Empty; }
}
Run Code Online (Sandbox Code Playgroud)

rhy*_*nix 6

为什么在 Ajax 中发送空时,我null在控制器中获得了值?

string是一个引用类型,它的默认值为null。在ModelBinder如果没有值在请求中提供设置属性的默认值。

有没有更好的方法和更优雅的方式来解决我的问题,如下所示?

  1. 您可以使用 注释该属性[DisplayFormat(ConvertEmptyStringToNull = false)],以便保留空字符串值。

  2. 您可以编写一个ModelBinder设置ConvertEmptyStringToNull为的自定义false,并在全球范围内应用它。

public class NullStringModelBinder : DefaultModelBinder {
    public override object BindModel(ControllerContext controllerContext,
                                     ModelBindingContext bindingContext) {
        bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
        return base.BindModel(controllerContext, bindingContext);
    }
}

//register it in Application_Start()
ModelBinders.Binders.Add(typeof(string), new NullStringModelBinder());
Run Code Online (Sandbox Code Playgroud)


Rah*_*rma 5

这一特定的更改已记录在此处,它是 的重大更改之一MVC 1.0。将空字符串绑定到 s 的逻辑由. 使用的属性null控制。ModelMetadata.ConvertEmptyStringToNullDefaultModelBinder

现在,如果您不想注释所有属性,您可以创建自定义模型绑定器:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

并将其设置在您的Global.asax

ModelBinders.Binders.DefaultBinder = new EmptyStringModelBinder();
Run Code Online (Sandbox Code Playgroud)

或者在你的具体行动中:

[HttpPost]
public JsonResult ActionSubmit([ModelBinder(typeof(EmptyStringModelBinder))SampleViewModel model)
Run Code Online (Sandbox Code Playgroud)

为什么要这样做?

这样做是因为 a 的默认值string是anull并且所有引用类型的默认值是。因此,框架的这种变化可能是合理的。但另一方面,我们应该尽量避免空值,因此我们需要编写一个自定义模型绑定器来避免这种情况。stringreference typenull

有一个问题为什么字符串类型的默认值是null而不是空字符串?。您可以查看此内容以更多地了解进行此更改的原因。

根据@Anton:c# 8.0您可以打开空检查以避免NullReferenceException并设置为引用类型默认值而不是null


Ngu*_*ong 5

我决定从@Rahul Sharma@rhytonix 的答案中进行总结,并为您提供示例和更详细的解释。

  1. 为什么当我在 Ajax 中发送空值时,我的控制器中得到空值?

这只是因为MVC 2.0默认将字符串初始化为 null。更准确地说,如果一个empty字符串没有值,那么 .NET 将设置它的默认值。默认字符串(属于引用类型)是null

更多详细信息,请参阅模型字符串属性绑定重大更改

  1. 有没有更好、更优雅的方法来解决我的问题,如下所示?

有一些方法可以将 String 属性绑定为string.Empty而不是null

1. 从 C# 6 开始,您可以使用DefaultValueAttribute为自动属性设置初始值,如下所示

public string LastName => string.Empty; 
Run Code Online (Sandbox Code Playgroud)

基本上,这种方式与帖子中提到的OP解决方案相同,只是更优雅。

IModelBinder2.通过继承内部对象DefaultModelBinder并将其值更改为 false 来自定义默认实现。ConvertEmptyStringToNullModelMetaData

public sealed class EmptyStringModelBinder : DefaultModelBinder 
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
        return base.BindModel(controllerContext, bindingContext);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在Application_Start()方法中Global.asax.cs你需要像下面那样完成

protected void Application_Start()
{
    ModelBinders.Binders.DefaultBinder = new EmptyStringModelBinder();
    RegisterRoutes( RouteTable.Routes );
}
Run Code Online (Sandbox Code Playgroud)

3.使用DisplayFormatAttribute.ConvertEmptyStringToNull 属性,如下所示

[DisplayFormat(ConvertEmptyStringToNull = false)]
public string LastName { get; set; }
Run Code Online (Sandbox Code Playgroud)

仅仅因为在ModelMetadata中

true如果空字符串值自动转换为null; 否则,false默认为true