使用jQuery向MVC操作方法提交数据

10
我正在尝试通过以下方法使用jQuery Ajax将数据发布到MVC操作。但在控制器内,所有模型属性始终为null。不确定我漏掉了什么。 .CSHTML
<form id="MyForm">
<input name="PersonName" type="text" />
<input name="Address" type="text" />
<select name="States" multiple="multiple">
    <option value="TX">Texas</option>
    <option value="OK">Oklahoma</option>
    <option value="OH">Ohio</option>
</select>
<select name="Status">
    <option value="1">Active</option>
    <option value="2">Deleted</option>
    <option value="3">Pending</option>
</select>
<input type="button" value="Save" id="Save" />

JavaScript

$(function () {
$("#Save").click(function (e) {
    var dataToPost = $("#MyForm").serialize()
    $.ajax(
    {
        type: "POST",
        data: JSON.stringify(dataToPost),
        url: "Working/Save",
        contentType: 'application/json; charset=utf-8'
    })
  })
})

控制器

public class WorkingController : Controller
{
    // GET: Working
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Save(WorkingModel model)
    {
        // All model properties are null here????

        return Json("Success");
    }
 }

模型

public class WorkingModel
{
    public string PersonName { get; set; }
    public string Address { get; set; }
    public string[] States { get; set; }
    public string Status { get; set; }
}

编辑1
我已经添加了上面的模型。这是当我点击保存时序列化数据和JSON stringify数据的内容。

序列化数据

"PersonName=Foo&Address=123+Test+Drive&States=TX&Status=1"

使用JSON.Stringify

"\"PersonName=Foo&Address=123+Test+Drive&States=TX&Status=1\""

我已经尝试添加HttpPost属性和[FromBody]属性,但没有成功。

我认为我不必将返回类型从ActionResult更改为JsonResult

另外,URL是正确的,因为调试器正在命中内部操作方法,在那里我可以QuickWatch模型属性。

请注意,如果我创建JSON对象并像下面这样发布它,则可以正常工作:

var dataToPost = {
    PersonName:'Foo',
    Address: '123 Test Drive',
    State: 'TX',
    Status: 1
 }

展示模型或 WorkingModel 并指出您在浏览器控制台中遇到的错误(我假设是由于不正确的URL而导致的404错误)。 - user3559349
移除 contentType: 'application/json; charset=utf-8' 并使用 data: dataToPost。您还应该使用 url: '@Url.Action("Save", "Working")' 确保您的URL相对于页面正确生成。 - user3559349
@StephenMuecke 你重复了我在答案中提出的建议 :). 这意味着我们思考方式相同,所以我们可以成为朋友 :). DotNetFiddle做得很好,我喜欢它。只是一个小提示:@Url.Action("Save", "Working")不是必需的,只要像OP一样使用正确的路径就可以了。然而,在大多数情况下,出于各种原因,使用@Url.Action("Save", "Working")是正确的方法。我只想提到两点:1)解耦物理路径和代码,2)集中路径生成。 - Racil Hilan
@RacilHilan,应该始终使用@Url.Action()。 OP当前的url是Working/Save,但根据当前页面可能需要为/Working/SaveUrl.Action()将始终生成正确的url。 - user3559349
@StephenMuecke 但我已经同意你的观点,甚至给出了两个重要原因,不算很多。我想说的是“应该”和“必须”是不同的。这意味着对于OP来说,这是一个非常好的建议,可能会为他节省一些麻烦,但它不会解决他在这个特定问题中面临的问题,因为他已经执行了操作,所以URL是正确的,而不是问题所在。再次,我完全同意你的建议。 - Racil Hilan
显示剩余3条评论
4个回答

14

你的JavaScript/jQuery代码可以大幅简化,这可能是最好的选择:

$(function () {
    $("#MyForm").on('submit', function (e) {

        e.preventDefault() // prevent the form's normal submission

        var dataToPost = $(this).serialize()

        $.post("Working/Save", dataToPost)
            .done(function(response, status, jqxhr){ 
                // this is the "success" callback
            })
            .fail(function(jqxhr, status, error){ 
                // this is the ""error"" callback
            })
    })
})

为了防止表单被非按钮元素提交,应该处理表单的onsubmit事件,而不是按钮的onclick事件。在这种情况下,我们希望阻止表单默认的提交行为,因为我们使用AJAX提交表单。

.serialize()已经正确地处理了表单的编码,因此您不需要对表单值进行JSON编码。这样做很可能是导致模型绑定器在处理请求时无法重新构建模型的原因。

$.post是一个包装常见设置工作的辅助函数,您需要用它来进行$.ajax操作 - 这里显示的版本需要POST到的URL和要POST的数据。如果您的jQuery代码在视图中的脚本元素内部,则可能需要使用Url.Action()辅助函数 - 它将根据您的路由规则构建正确的URL。如果您选择采用该方法,则可以使用类似以下内容:

$.post('@Url.Action("Save", "Working")', dataToPost)

然后,我们使用相关帮助程序处理成功响应(任何具有HTTP-200状态代码的内容)和失败响应(基本上是其他所有内容)。在这些帮助程序中你想要做什么是由你自己决定的。


7
我认为您应该提交标准的 HTML 表单数据,而非 JSON 数据。因此,请更改以下行:
```html ```
data: JSON.stringify(dataToPost),
contentType: 'application/json; charset=utf-8'

to

data: dataToPost,
contentType: 'application/x-www-form-urlencoded; charset=UTF-8'

注意:你也可以省略第二行,因为它是$.ajax的默认contentType。 根据jQuery文档
编辑1:这是对您的评论和编辑的回应。
我想告诉你的是,您的Ajax发送的数据必须与您处理的操作接收到的数据匹配。您的模型属性为null的原因是两者不匹配。
您没有发布操作的代码,因此我们不知道您如何处理数据,但现在从您的编辑1中可以看出,您正在处理接收到的数据作为JSON数据,因为当您发送真实的JSON数据时它能够工作。
所以您需要执行以下两个步骤之一:
1- 发送真正的JSON数据:仅使用JSON.stringify并不意味着您的数据现在是合适的JSON数据。正如您发现的那样,JSON.stringify只是用引号将您的字符串包装使其成为有效的JSON字符串,这就是全部。但是这不是您的操作所期望的,它期望一个JSON对象。要发送JSON对象,您可能需要编写一个函数,逐个获取表单字段并构建JSON对象,然后调用此函数而不是JSON.stringify
2- 发送标准表单数据:这就是我在上面的答案中建议的。为了使其工作,只需删除您操作中处理JSON对象的所有代码即可。 MVC默认设计用于处理标准表单数据,因此您不需要任何额外的处理。只需发送标准表单数据即可。
注意:Ajax不需要以JSON格式发送/接收数据。 JSON格式在许多情况下用于交换数据非常有用,但是您可以选择适合特定场景的格式。例如,您的操作正在返回一个JSON对象作为结果:return Json("Success")。如果您只想发送简单的结果(成功与否),则可以返回一个简单的字符串(例如return "Success")或甚至是布尔值/整数(例如return "True"return "1")。 Jason对象需要对它们从字符串进行解析的额外处理。虽然这种处理非常快速和高效,但如果您不需要发送其他信息,则解析和处理简单数据类型如字符串、布尔值或整数比解析JSON对象更快。

OP正在提交JSON。这就是data: JSON.stringify(dataToPost)的作用! - user3559349
@StephenMuecke 谢谢您的提醒。当然,您对那行代码的理解是正确的,但我想说的是他不应该提交 Json。我应该在我的回答中包含那行代码。我会更新我的回答。 - Racil Hilan
@RacilHilan 感谢您的回复。模型属性与 HTML 控件的名称匹配,它们是可为空的字符串。此外,无论操作方法在接收模型后如何处理模型,对于操作方法来说,它只是一个简单的 C# 对象,这有什么关系呢? - LP13
1
嗯,你说得很有道理,关于之后的处理。就像我说的,我们看不到你的动作代码,所以那都是猜测。但除此之外,我的答案仍然适用,所以你试过我上面给出的解决方案了吗? - Racil Hilan
请注意,输入的类型不是“提交”。 - LP13
当然可以,因为您正在使用Ajax提交它。我的建议是,您的Ajax提交应该模仿标准表单提交。 - Racil Hilan

5
我只需要从文章中删除内容类型并将其不转化为字符串即可使其正常运行。
$(function () {
  $("#Save").click(function (e) {
  var dataToPost = $("#MyForm").serialize()
  $.ajax(
  {
    type: "POST",
    data: dataToPost,
    url: "Working/Save"
  })
 })
})

1
哈!非常聪明,甚至没有考虑将其作为表单数据提交。顺便说一下,此解决方案以以下格式向服务器发送数据: PersonName=a&Address=b&States=TX&States=OK&Status=2而不是JSON。但这完全没问题:)。您会发现,随着表单变得更加复杂,手动构建JS对象(就像我的答案中所示)将更有效,因为自动化的东西如$.serialize不能很好地进行定制。 - Michael Crook
@MichaelCrook 谢谢。你的回答让我再次思考 :) 到目前为止,我们只提交了少量数据(5-6个字段),这就是为什么我们手工构建JSON对象的原因。对于复杂页面(30-40个输入+网格),我不想手工构建JSON对象。我认为表单序列化会解决问题。我们知道 $.serialize 方法有什么具体问题吗? - LP13
如果您的数据显示方式不能直接从$.serialize映射到ASP.NET模型,那么您将被迫像我一样将其映射到中间模型。但是,如果您开始使用类似于此类的大型表单,我建议您学习Knockout.js,它是一个用于Web的数据绑定框架。这样,您的HTML将绑定到JS对象,然后您只需JSON.stringify您的js模型,它就会自动准备好所有表单数据! - Michael Crook
1
@MichaelCrook,那是无稽之谈。如果视图是基于模型构建的HtmlHelpers,则始终可以正确使用.serialize() - user3559349
1
@user3862378,由于Tieson T.之前提供了正确的答案,你应该接受它,或者至少点赞它。 - user3559349

1

将之前的答案文本移动到pastebin,因为我的答案是错误的,正确答案如下:

我刚刚看到了你的编辑,你的问题是JSON格式看起来有些奇怪: "\"PersonName=Foo&Address=123+Test+Drive&States=TX&Status=1\"" 这将无法转换为WorkingModel。

我的建议是创建一个自定义的JS对象并将其发布。我只是启动了一个VS MVC项目并进行了制作,一切都在运作 :)
$(function() {
    $("#Save").click(function(e) {
        var personName = $("[name='PersonName']").val();
        var address = $("[name='Address']").val();
        var states = $("[name='States']").val();
        var status = $("[name='Status']").val();
        var dataToPost = {
            PersonName: personName,
            Address: address,
            States: states,
            Status: status
        };

        $.ajax(
        {
            type: "POST",
            data: JSON.stringify(dataToPost),
            url: "Save",
            contentType: 'application/json; charset=utf-8'
        });
    });
});

希望这有所帮助!

1
您不需要指定 [HttpPost](ajax选项包括 type: "POST")。您不需要将 ActionResult 更改为 JsonResultJsonResult ActionResult)。您不需要使用 [FromBody](那与webapi有关)。 - user3559349
为什么不指定 [HttpPost]?我很确定(但不是完全确定)save 不是 MVC 可以自动转换的方法名。JsonResult 并不是必需的,但它更有意义,因为它返回了一个 JsonResult。但也许你是正确的,因为他正在调用这个方法,只是没有任何东西与之一起传递。尝试查看 JS 使用 JSON.stringify 创建了什么,也许它没有工作,你传递了 null 上去。 - Michael Crook
1
你可以(并且应该)包含 [HttpPost] 属性,但在这种情况下由于 ajax 调用而不是必需的。同样适用于 JsonResult(您可能应该使用,但不必要)。OP 的问题与您在答案中提供的任何内容无关。可能是 URL 不正确(应为 /Working/Save"),或者模型仅有字段(没有 get; set;),但在提供更多信息之前谁知道呢。总之,在任何情况下,OP 应删除 contentType: 并且不要将数据 stringify - user3559349
@MichaelCrook 谢谢!这就是我们现在所做的,通过手动创建JSON对象并发布它来实现。然而,如果我们必须发布大型对象,那么这将是一个耗时的过程。这就是为什么我想要序列化表单并发布它的原因。还请注意,按钮的类型不是“提交”。 - LP13
原因是$.serialize不会将数据转换为JSON,它的转换结果可以用于URI提交(例如:www.yoursite.com/post?PersonName=a&Address=b&States=OK&States=OH&Status=3,详见:https://jsfiddle.net/n7rpzr6x/)。如果你想要将表单自动转换为JSON,请参考这个stackoverflow问题,它的回答非常棒(拥有超过1000个赞),链接如下:https://dev59.com/f3M_5IYBdhLWcg3w4nfb - Michael Crook
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接