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

Posted

技术标签:

【中文标题】Ajax 传递空值但控制器在 ASP.NET MVC 中为空【英文标题】:Ajax passing empty value but Controller get null in ASP.NET MVC 【发布时间】:2020-05-24 11:56:55 【问题描述】:

我正在使用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; 

控制器

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

Ajax

$('.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);
            );
         );

如您所见,我发送空值,但在控制器端,我得到 null(响应值始终“LastName 为 null”):

问题

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

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

public string FirstName

   get  return _firstName; 
   set  _firstName = value ?? string.Empty; 

【问题讨论】:

如果您从 Ajax 调用中传递了一些值 - 您的控制器是否获得了这些值? 是的,先生。这是一个很好的问题,我刚刚用更多细节更新了我的问题。请看一下。 试试FirstName: '""'(单引号内有双引号)。 @史蒂夫格林。不幸的是,它仍然不起作用ibb.co/7gWjjmL 做一件事,在视图上创建对象,除了全名之外,不要在其中放置任何值意味着 objectname.FullName="Phong_Nguyen" 然后传递不要为其余元素放置任何值,您将获得 "" 在你的控制器 【参考方案1】:

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

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

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

    您可以使用[DisplayFormat(ConvertEmptyStringToNull = false)] 对属性进行注释,从而保留空字符串值。

    您可以编写自定义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());

【讨论】:

感谢您的回复。 2种解决方法看起来不错。但是,如果请求中没有提供任何值,听起来好像真的不适合我的问题。如您所见,我发送的是空值。无论如何+1。 “无价值”是指一个空字符串。 ModelBinder 的默认行为是将传入的空字符串转换为null,因此我们必须显式覆盖该行为。检查第 50 行的 ValueProviderResult implementation 以了解它如何处理字符串转换。 ValueProviderResult负责将输入转换为Action的参数类型,如果修剪后的输入字符串长度为0,则将目标设置为null 你能告诉我.Net为什么会有这样的变化吗?我的意思是我们都知道我们应该有一个空值来避免NullReferenceException而不是null,对吧? @Phong 显然modelBuinder默认使用string.IsNullOrEmpty来检查字符串值 @Phong 根据this,决定与WebForms 将空字符串视为null 的行为保持一致。而且我认为另一个原因是也与引用类型的期望保持一致——正如我在原来的帖子中所说的那样——例如默认情况下,空数组的输入也被转换为null,因为它也是一个引用类型,因为将一些引用类型初始化为非空值而将其他引用类型转换为null是没有意义的,而是它们都应该具有相同的统一行为。【参考方案2】:

此特定更改已记录在 here 中,它是 MVC 1.0 的重大更改之一。这种将空字符串绑定到nulls 的逻辑由DefaultModelBinder 使用的ModelMetadata.ConvertEmptyStringToNull 属性控制。

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

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

并将其设置在您的Global.asax:

ModelBinders.Binders.DefaultBinder = new EmptyStringModelBinder();

或者在你的具体行动中:

[HttpPost]
public JsonResult ActionSubmit([ModelBinder(typeof(EmptyStringModelBinder))SampleViewModel model)

为什么要这样做?

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

有一个关于为什么default value of the string type null instead of an empty string? 的问题。您可以仔细阅读以了解更多关于为什么进行此更改的信息。

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

【讨论】:

你能告诉我.Net为什么会有这样的变化吗?我的意思是我们都知道我们应该有一个空值来避免NullReferenceException 而不是null,对吧? @Phong 这样做是因为string 的默认值为null,并且因为stringreference type,并且所有引用类型的默认值为null。因此,框架的这种变化可能是合理的。但另一方面,我们应该尽量避免空值,因此我们需要编写一个自定义模型绑定器来避免这种情况。你可以看看这个问题:***.com/questions/14337551/… @Phong 这是 C# 设计的。现在在 c# 8.0 中,您可以打开 null cheks 以避免 NullReferenceException 并设置为引用类型默认值而不是 ``null 是的,我知道我们需要完全避免空值。那么为什么.Net会自动从空转换为null,那么现在我们又要转换一个,不知道为什么我们得到了上面帖子那样的奇怪值。 @Phong .Net 自动将空转换为空,因为当遇到空的 string 时,它会设置 string 的默认值,并且由于 stringreference type 并且所有引用类型的默认值为null,它会将它们更改为null,这是框架的预期行为。【参考方案3】:

当你在 C# 中声明一个字符串变量时,它的值是null,直到被赋值。

当通过表单提交发送数据时,任何未输入信息的字段都将作为空字符串发送。 没有提供任何信息的最佳模拟是null,因此它将这些值设置为null(或者更可能的是,根本不设置值)。

MVC 无法区分空字符串(因为没有提供信息)和空字符串(因为这是在传输之前在 javascript 中分配的值)。它只知道其中一个字段没有信息,因此该值应该是null

【讨论】:

感谢您的回答。不幸的是,我找不到比上述答案更有用的新信息。因此,您可以清楚地在解决方案中添加更多信息。【参考方案4】:

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

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

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

更多详情Model String Property Binding Breaking Change

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

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

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

public string LastName => string.Empty; 

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

2。通过从DefaultModelBinder 继承并将内部ModelMetaData 对象上的ConvertEmptyStringToNull 值更改为false 来自定义IModelBinder 的默认实现。

public sealed class EmptyStringModelBinder : DefaultModelBinder 

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    
        bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
        return base.BindModel(controllerContext, bindingContext);
    

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

protected void Application_Start()

    ModelBinders.Binders.DefaultBinder = new EmptyStringModelBinder();
    RegisterRoutes( RouteTable.Routes );

3.像下面这样使用DisplayFormatAttribute.ConvertEmptyStringToNull Property

[DisplayFormat(ConvertEmptyStringToNull = false)]
public string LastName  get; set; 

只是因为在ModelMetadata

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

【讨论】:

以上是关于Ajax 传递空值但控制器在 ASP.NET MVC 中为空的主要内容,如果未能解决你的问题,请参考以下文章

JQuery ajax - 如何传递具有空值的数组参数

通过 Ajax 将布尔值传递给 asp.net api 控制器

将多个参数从 ajax post 传递到 asp.net mvc 控制器

使用 Jquery Ajax 将数组传递给 asp.net C# 控制器

C# ASP.net MVC Ajax MySQL DATATABLES 列搜索无法将搜索值传递给控制器

如何使用 AJAX JQuery ASP .NET MVC 4 将 FormCollection 和文件传递给控制器