如何在ASP.Net MVC中将数组传递给action方法?

时间:2016-03-27 13:55:04

标签: jquery json ajax asp.net-mvc-5

我正在开发一个ASP.Net MVC5应用程序,我试图通过AJAX请求将JSON数据传递给控制器​​的操作方法。 JSON数据包含许多表单值。由于我试图创建一个主数据表单,其中数据要在数据库中以一对多格式发布,因此我创建了一个这样的对象:

var salesmain = { 
    "SalesId": "", 
    "ReferenceNo": "", 
    "SalesDate": "", 
    "SalesPerson": "", 
    "SalesSubs": [] 
};

请注意,在上面的数据中,SalesSubs属性是以这种方式声明的数组:

var salessub = {    
    "SalesId": "", 
    "ItemName": "",
    "Qty": "",
    "UnitPrice":""
};

所以,现在我发现一个salesmain可以包含许多salessubsalessub数组。在我推送一些值并将AJAX请求传递给action方法后,它将SalesSubs数组作为null。我在那里放了一个断点来检查它:

enter image description here

在上面的图片salesmain.SalesSubs中,这是一个数组null,这就是我尝试将数据传递给动作的方式:

$.ajax({
    url: '/Sales/Create',
    data: addRequestVerificationToken(salesmain),
    type: 'POST',
    dataType: 'json',
    traditional : true,
    success: function (result) {
        if (result.Success == "1") {
            window.location.href = "/Sales/Index";
        }
        else {
            alert(result.ex);
        }
    }
});

这就是我在SalesSubs数组中推送值的方式:

salesmain.SalesId = $("#SalesId").val();
salesmain.ReferenceNo = $("#ReferenceNo").val();
salesmain.SalesDate = $("#SalesDate").val();
salesmain.SalesPerson = $("#SalesPerson").val();

$('.tbl').DataTable().rows().data().each(function (value, index) {
    salessub.ItemName = value[0];
    salessub.Qty = value[1];
    salessub.UnitPrice = value[2];
    salesmain.SalesSubs.push(salessub);
});

在上面的代码中,我使用了jQuery的datatable插件来从DataTable中获取整个salessub数据。我认为salesmain在执行ajax请求之前包含一个完整的有效数据,但不知何故ajax请求在执行后没有携带salesmain对象的数组数据(SalesSubs)。

我的AJAX部分代码有什么问题吗?使用AJAX请求将JSON数据传递给action方法的正确方法是什么?

编辑:

这是SalesMain.cs模型:

public class SalesMain
{
    [Key]
    public int SalesId { get; set; }
    public string ReferenceNo { get; set; }
    public DateTime SalesDate { get; set; }
    public string SalesPerson { get; set; }

    public virtual ICollection<SalesSub> SalesSubs { get; set; }
}

和SalesSub.cs模型:

public class SalesSub
{
    [Key, Column(Order = 0)]
    public int SalesId { get; set; }
    [Key, Column(Order = 1)]
    public string ItemName { get; set; }
    public int Qty { get; set; }
    public decimal UnitPrice { get; set; }
    public virtual SalesMain SalesMain { get; set; }
}

和SalesController创建方法:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "SalesId,ReferenceNo,SalesDate,SalesPerson")] SalesMain salesmain)
    {
        try
        {
            if (ModelState.IsValid)
            {
                // Perform Update 
                if (salesmain.SalesId > 0)
                {

                    var CurrentsalesSUb = db.SalesSubs.Where(p => p.SalesId == salesmain.SalesId);

                    foreach (SalesSub ss in CurrentsalesSUb)
                        db.SalesSubs.Remove(ss);

                    foreach (SalesSub ss in salesmain.SalesSubs)
                        db.SalesSubs.Add(ss);

                    db.Entry(salesmain).State = EntityState.Modified;
                }
                //Perform Save 
                else
                {
                    db.SalesMains.Add(salesmain);
                }

                db.SaveChanges();
                return Json(new { Success = 1, SalesID = salesmain.SalesId, ex = "" });
            }
        }
        catch (Exception ex)
        {
            return Json(new { Success = 0, ex = ex.Message.ToString() });
        }

        return Json(new { Success = 0, ex = new Exception("Unable to save").Message.ToString() });
    }

如果在发送ajax请求之前我console.log(salesmain),这就是我得到的:

enter image description here

所以,我认为上述数据可以通过ajax请求移动,但现在当它到达动作salesmain.SalesSubs变为null时,只有SalesMain表填充在数据库中,并且SalesSub表保持为空(未实现映射)。

进一步修改:

添加contentType : 'application/json'data : JSON.stringify(salesmain)导致500内部服务器错误,在控制台窗口中检查后,错误显示:

The required anti-forgery form field “__RequestVerificationToken” is not present.

它真的不存在吗?因为我已经为此创建了一个函数:

function addRequestVerificationToken(data) {
        data.__RequestVerificationToken = $('input[name=__RequestVerificationToken]').val();
        return data;
    };

当我使用salesmaincontentType : "application/json"时,上述代码不会将令牌添加到JSON.stringify(),而是在删除这两个代码时添加。

1 个答案:

答案 0 :(得分:1)

使用JSON.stringify方法将js对象转换为json字符串化版本。同时将contentType指定为“application / json”。

$.ajax({
        data: JSON.stringify(salesmain),
        contentType : "application/json",
        success: function (result) { alert(result); }
       // Your existing code
});

我还建议使用Url.Action之类的Html帮助程序来生成操作方法的正确相对路径,而不是对其进行硬编码。

url: '@Url.Action("Create","Sales")',

如果您的js代码位于剃刀视图中,则上述应该有效。如果您的代码位于外部js文件中,请使用this post中描述的解决方案。

编辑:当您使用contentType='application/json'发送数据时,您的RequestVerificationToken无法正常工作,因为代码希望表单正常提交(其中contentType设置为默认值“{{1 }}“)。因此,您可以在HttpPost操作方法中禁用application/x-www-form-urlencoded

此外,我发现您使用了Bind属性仅包含某些属性。您需要将[ValidateAntiForgeryToken]属性添加到包含列表中,以便模型绑定也适用于该属性。

以下代码应该可以正常工作。

发送数据的客户端代码。

SalesSubs

服务器代码将是

var salesmain = { SalesSubs: [] };
salesmain.SalesId =33;
salesmain.ReferenceNo ="Test";
salesmain.SalesDate ="12/12/2004";
salesmain.SalesPerson = "Shyju";

var salessub = {};
salessub.ItemName = "Test";
salessub.Qty = 2;
salessub.UnitPrice = 25.33;
salesmain.SalesSubs.push(salessub);
console.log(salesmain);

$.ajax({
    url: '/Sales/Create'  ,
    data: JSON.stringify(salesmain),
    type: 'POST',
    contentType: 'application/json',
    success: function (result) {
        console.log('response');
        console.log(result);
    }
});

best way to prevent over posting是使用视图模型,其中包含您从视图中绝对需要的属性。我个人更喜欢这种方法在视图和操作方法之间传输数据,而不是使用带有Bind属性的ORM生成的实体类。