使用jQuery向ASP.NET API控制器发送POST请求

11

我有一个通过jquery生成的表单:

 $.get("/api/get/getListItems", function (data) {
                var table = "";
                table += "<table>";
                $.each(data, function (y, z) {
                    console.log(z);
                    table += '<tr>';
                    $.each(this, function (k, v) {
                        table += '<td><input type="text" name="' + k + '" id="' + k + '" value="' + v + '" /></td>';
                    });
                    table += '<td><input type="checkbox" name="selected" id="selected" /></td>';

                    table += '</tr>';
                });
                table += '<tr><td><input type="submit" id="submit" name="submit" value="Save To Database" /></td></tr>';
                table += '</table>';
                $('#form').html(table);
            });

而且它生成这个HTML(10行输入字段,7列和1个复选框):http://jsfiddle.net/8zpr2fkL/1/

当提交按钮被点击时,我会提交表单:

$("#form").submit(function (event) {
        $.post("/api/update/", $("#form").serialize(), alert('success'));
    });

现在我正在将数据传递给我的ASP.NET API控制器:

[HttpPost]
        public dynamic Post([FromBody]CellModel cells)
        {
                UpdateClass jobs = new UpdateClass();
                return jobs;
        }

这是我的CellModel类:

public class CellModel
    {
        public uint scheduleTaskID { get; set; }
        public string task { get; set; }
        public string baselineDate { get; set; }
        public string scheduledDate { get; set; }
        public string actualDate { get; set; }
        public string finishedDate { get; set; }
        public bool selected { get; set; }

        public override string ToString()
        {
            return scheduleTaskID.ToString();
        }
    }
我的问题是,当我点击提交按钮提交数据并在控制器方法上设置断点时,单元格计数为0,我漏掉了什么吗?我正在尝试将所有输入文本中的数据传递给控制器。 没有任何数据被传递到我的控制器。 我做错了什么?这是我尝试通过jquery $('#form').serialize() 传递的数据:
scheduleTaskID=194&task=Permit&baselineDate=6%2F23%2F2005+8%3A00%3A00+AM&scheduledDate=6%2F23%2F2005+8%3A00%3A00+AM&actualDate=6%2F23%2F2005+8%3A00%3A00+AM&finishedDate=&scheduleTaskID=195&task=Office+Files&baselineDate=7%2F13%2F2005+8%3A00%3A00+AM&scheduledDate=7%2F13%2F2005+8%3A00%3A00+AM&actualDate=7%2F13%2F2005+8%3A00%3A00+AM&finishedDate=&scheduleTaskID=196&task=Foundation&baselineDate=7%2F27%2F2005+8%3A00%3A00+AM&scheduledDate=7%2F27%2F2005+8%3A00%3A00+AM&actualDate=8%2F13%2F2005+8%3A00%3A00+AM&finishedDate=&scheduleTaskID=197&task=Framing&baselineDate=8%2F5%2F2005+8%3A00%3A00+AM&scheduledDate=8%2F5%2F2005+8%3A00%3A00+AM&actualDate=8%2F23%2F2005+8%3A00%3A00+AM&finishedDate=&scheduleTaskID=198&task=Finishes+Exterior&baselineDate=8%2F26%2F2005+8%3A00%3A00+AM&scheduledDate=8%2F26%2F2005+8%3A00%3A00+AM&actualDate=9%2F14%2F2005+8%3A00%3A00+AM&finishedDate=&scheduleTaskID=199&task=Drywall&baselineDate=9%2F2%2F2005+8%3A00%3A00+AM&scheduledDate=9%2F2%2F2005+8%3A00%3A00+AM&actualDate=9%2F16%2F2005+8%3A00%3A00+AM&finishedDate=&scheduleTaskID=200&task=Flooring&baselineDate=9%2F1%2F2005+8%3A00%3A00+AM&scheduledDate=9%2F1%2F2005+8%3A00%3A00+AM&actualDate=9%2F20%2F2005+8%3A00%3A00+AM&finishedDate=&scheduleTaskID=201&task=General+Finish&baselineDate=9%2F12%2F2005+8%3A00%3A00+AM&scheduledDate=9%2F12%2F2005+8%3A00%3A00+AM&actualDate=&finishedDate=&scheduleTaskID=202&task=Final+PDI&baselineDate=10%2F11%2F2005+8%3A00%3A00+AM&scheduledDate=10%2F11%2F2005+8%3A00%3A00+AM&actualDate=&finishedDate=&scheduleTaskID=203&task=Permit&baselineDate=4%2F6%2F2005+8%3A00%3A00+AM&scheduledDate=4%2F6%2F2005+8%3A00%3A00+AM&actualDate=4%2F6%2F2005+8%3A00%3A00+AM&finishedDate=

更新

我已经更改了:

$("#form").submit(function (event) {
            $.post("/api/update/", $("#form").serialize(), alert('success'));
        });
"to"
$("#form").submit(function (event) {
        var array = [];
        $('#form > table > tbody  > tr').each(function (elem) {
            var item = {};
            item.scheduleTaskID = $(this).find("td > #scheduleTaskID").val();
            item.task = $(this).find("td > #task").val();
            item.baselineDate = $(this).find("td > #baselineDate").val();
            item.scheduledDate = $(this).find("td > #scheduledDate").val();
            item.actualDate = $(this).find("td > #actualDate").val();
            item.finishedDate = $(this).find("td > #finishedDate").val();
            item.selected = $(this).find("td > #selected").val();
            array.push(item);
        });
        console.log(JSON.stringify(array));
        $.post("/api/update/", JSON.stringify(array), alert('success'), 'json');
    });

在我的控制台日志中,我的数据看起来像这样:

[{"scheduleTaskID":"203","task":"Permit","baselineDate":"4/6/2005 8:00:00 AM","scheduledDate":"4/6/2005 8:00:00 AM","actualDate":"4/6/2005 8:00:00 AM","finishedDate":"","selected":"on"},{"scheduleTaskID":"195","task":"Office Files","baselineDate":"7/13/2005 8:00:00 AM","scheduledDate":"7/13/2005 8:00:00 AM","actualDate":"7/13/2005 8:00:00 AM","finishedDate":"","selected":"on"},{"scheduleTaskID":"196","task":"Foundation","baselineDate":"7/27/2005 8:00:00 AM","scheduledDate":"7/27/2005 8:00:00 AM","actualDate":"8/13/2005 8:00:00 AM","finishedDate":"","selected":"on"},{"scheduleTaskID":"197","task":"Framing","baselineDate":"8/5/2005 8:00:00 AM","scheduledDate":"8/5/2005 8:00:00 AM","actualDate":"8/23/2005 8:00:00 AM","finishedDate":"","selected":"on"},{"scheduleTaskID":"198","task":"Finishes Exterior","baselineDate":"8/26/2005 8:00:00 AM","scheduledDate":"8/26/2005 8:00:00 AM","actualDate":"9/14/2005 8:00:00 AM","finishedDate":"","selected":"on"},{"scheduleTaskID":"199","task":"Drywall","baselineDate":"9/2/2005 8:00:00 AM","scheduledDate":"9/2/2005 8:00:00 AM","actualDate":"9/16/2005 8:00:00 AM","finishedDate":"","selected":"on"},{"scheduleTaskID":"200","task":"Flooring","baselineDate":"9/1/2005 8:00:00 AM","scheduledDate":"9/1/2005 8:00:00 AM","actualDate":"9/20/2005 8:00:00 AM","finishedDate":"","selected":"on"},{"scheduleTaskID":"201","task":"General Finish","baselineDate":"9/12/2005 8:00:00 AM","scheduledDate":"9/12/2005 8:00:00 AM","actualDate":"","finishedDate":"","selected":"on"},{"scheduleTaskID":"202","task":"Final PDI","baselineDate":"10/11/2005 8:00:00 AM","scheduledDate":"10/11/2005 8:00:00 AM","actualDate":"","finishedDate":"","selected":"on"},{"scheduleTaskID":"203","task":"Permit","baselineDate":"4/6/2005 8:00:00 AM","scheduledDate":"4/6/2005 8:00:00 AM","actualDate":"4/6/2005 8:00:00 AM","finishedDate":"","selected":"on"},{}]

在我的ASP.NET API Controller中,我将我的方法更改为:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using MvcApplication1.Models;

namespace MvcApplication1.Controllers
{
    public class UpdateController : ApiController
    {
        [HttpPost]
        public dynamic Post(List<CellModel> cells)
        {
                UpdateClass jobs = new UpdateClass();
                //jobs.PostScheduledTasks(cells);
                return cells;
        }

    }
}

我在Post方法的开头设置了断点,当它命中断点时,它说cells Count = 0。我只有在我的post调用后放置一个返回false并且响应为空[]才会看到网络调用。为什么数据没有传递给我的控制器?是因为表单是由jQuery生成的吗?

更新

仍然没有解决方案,我今天早上查看了我的网络调用,状态代码为301:

enter image description here


1
这不应该是一个CellModels列表吗?看起来你正在返回多个... 这可能解决不了问题,但这是需要注意的地方... - Dylan Corriveau
是的,我正在发送多个.... - user979331
如果您使用Chrome浏览器,可以使用检查器和网络选项卡,准确地查看发送回控制器的内容。我建议在Post行之后设置断点,以确保cells已填充并成功中断。 - Jason
另外,我使用这个工具来测试我的控制器/REST API: https://chrome.google.com/webstore/detail/dev-http-client/aejoelaoggembcahagimdiliamlcdmfm - Jason
这可能看起来有些愚蠢,但如果您删除 dynamic 返回类型并返回像 object(或更好的是 HttpResponseMessage)这样的东西会发生什么? - entropic
显示剩余2条评论
3个回答

23

不要使用$.post,使用ajax post并将内容类型设置为"application/json; charset=utf-8"

var data = JSON.stringify(array);
$.ajax({
  url:"/api/update/",
  type:"POST",
  data:data,
  contentType:"application/json; charset=utf-8",
  dataType:"json",
  success: function(data){
    console.log(data);
  }
});

问题在于您需要告诉Web服务器,您正在发送JSON,而使用$.post是不可能的。

这很正常,我也曾经苦苦挣扎(有时我仍然会忘记),在这里,您可以看到您必须使用$.ajax。

Jquery - 如何使$.post()使用contentType=application/json?


2
它可以工作,但是时间空间性能比使用 $.post 的解决方案差(请查看我的答案)。您正在使用 foreach 循环将表单元素转换为 JSON,并且重复使用 jQuery 进行 DOM 搜索,这是完全不必要的。如果您遵循下面概述的规则,只需序列化表单并进行 post 就很简单了。 - Faris Zacina
@TheZenCoder,我不知道你的方法,但是在阅读了所有链接后,我发现客户端性能的损失是令人不齿的(说实话)。也许我没有处理过大型表单,也不知道其他情况,但是对于一般的表单来说,你所提出的方法并不会有任何损失。无论如何,我投票支持你的方法。祝你周末愉快:D - dariogriffo
1
@dariogriffo,也许性能差异不是很大,但使用serialize()函数序列化表单和$.post方法的代码更简单,因为你不必处理JSON。无论如何,你的方法也可以实现并且被广泛接受。了解同一问题的多种解决方案总是有益的;)感谢你的点赞! - Faris Zacina

4

虽然 @dariogriffo 提供了另一种方法,但我想用你最初的方法和 $.post 来给你一个完整的解决方案。

你最初使用表单序列化的方法是正确的,因此以下代码是正确的:

$("#form").submit(function (event) {
   $.post("/api/update/", $("#form").serialize(), alert('success'));
});

然而,由于您的动态表单没有遵循 ASP.NET MVC 默认模型绑定器所期望的输入字段命名约定,因此此方法无法奏效。因此,您序列化的表单是默认模型绑定器无法绑定到“cells”模型上的。这就是为什么在进行 POST 时控制器中没有任何单元格的原因。
为了澄清其含义,如果您要发布到常规 MVC 5 控制器,则 ASP.NET 期望每个对应于模型属性的输入字段具有以下名称格式:
actionattributename[index].propertyname

如果您要发布到Web API 2控制器,则应该是:

[index].propertyname

由于您的操作属性名为cells,并且它具有scheduledTaskID属性,在将数据提交到WebAPI控制器时,其中一个输入应该如下所示:

<input type="text" name="[0].scheduleTaskID" id="scheduleTaskID" value="194">

在构建可绑定的表单时,需要遵循一些规则。你可以在这里找到一篇关于此的不错博客文章:http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
如果你按照这个表单约定,就可以将序列化后的表单提交到你的控制器,期望返回一个 List<CellModel> cells,而无需使用其他答案中提到的代价更高的JSON方法。
下面是一个示例表单的JSFiddle链接,它按照Web API 2默认绑定器规则正确构建了表单:http://jsfiddle.net/8zpr2fkL/12/
你可以尝试使用 $.post 将这个表单提交到你的Web API控制器中,它应该能完美地工作!

我也喜欢这个解决方案。 - dariogriffo

0

我知道,这个问题已经通过使用.ajax而不是.post得到解决。我想分享一下,因为我已经使用.post解决了这个问题。如上所述,由于它以内容类型Content-Type:application/x-www-form-urlencoded; charset=UTF-8进行发布,因此post方法中的参数cells将包含count = 0

要解决这个问题,您必须手动捕获请求对象并获取post数据,然后进行反序列化并获取对象作为List<CellModel>。我已经使用OP发布的所有代码,并只修改了post方法,如下所示,它可以工作。

[HttpPost]
public dynamic Post(List<CellModel> cells)
{
    string content = string.Empty;
    if (HttpContext.Current.Request.InputStream.CanSeek)
    {
        HttpContext.Current.Request.InputStream.Seek(0, System.IO.SeekOrigin.Begin);
    }
    using (System.IO.StreamReader reader = new System.IO.StreamReader(HttpContext.Current.Request.InputStream))
    {
        content = reader.ReadToEnd();
    }
    if (!string.IsNullOrEmpty(content))
    {
        // Deserialize and operate over cells.
        try
        {
            var obj = Newtonsoft.Json.JsonConvert.DeserializeObject(content, typeof(List<CellModel>));
        }
        catch (Exception ex)
        {
            return ex;
        }

    }
    return cells;
}

这是我在调试时得到的结果。

enter image description here

enter image description here


假设你的API中有120个POST端点,这是完全无法维护的。 - dariogriffo
我不知道OP会用这个做什么,如果你知道,请告诉我。我只是尝试使用.post来解决问题,这是可行的。但是,.ajax是处理WebAPI中使用的HTTPPost的最佳方式。 - Arindam Nayak

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