【问题标题】:How to pass array to action method in ASP.Net MVC?如何将数组传递给 ASP.Net MVC 中的操作方法?
【发布时间】:2016-03-28 01:42:32
【问题描述】:

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

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

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

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

所以,现在我知道一个salesmain 可以包含许多salessub 或salessub 的数组。在我将一些值放入其中并将 AJAX 请求传递给操作方法后,它的 SalesSubs 数组为 null。我已经通过在那里放置一个断点来检查它:

在上图中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 中获取整个 salessub 数据。我认为salesmain在ajax请求执行之前包含了完整的有效数据,但是ajax请求执行后不知何故没有携带salesmain对象的数组数据(SalesSubs)。

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

编辑:

这是 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),这就是我得到的:

所以,我认为上面的数据可以通过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;
    };

当我使用contentType : "application/json"和JSON.stringify()时,上面的代码没有将令牌添加到salesmain,当这两个被删除时它会添加。

【问题讨论】:

  • 这听起来像是 ModelBinder 的问题。您能否在请求中添加您发送的确切 JSON 字符串以及接收操作的签名(以及类大纲)
  • 感谢您的回复 :),我已经更新了我的问题,请看一下。
  • 在 ajax 请求中添加 contentType: 'application/json, charset=utf-8'
  • @Santosh,我也试过这个,添加 contentType: 'application/json' 甚至不将数据传递给动作,在客户端出现错误The required anti-forgery form field "__RequestVerificationToken" is not present,它没有读取令牌字段,为了论证,我删除了 [ValidateAntiForgery] 动作过滤器,然后检查它,SalesSubs 仍然是null :(
  • 您的Bind 属性不包括SalesSubs,因此模型绑定器在解析模型时会忽略它

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


【解决方案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 代码位于 razor 视图中,上述内容应该可以工作。如果您的代码在外部 js 文件中,请使用this post 中描述的解决方案。

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

我还看到您使用 Bind 属性仅包含某些属性。您需要将 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);
    }
});

服务器代码是

[HttpPost]
public ActionResult Create([Bind(Include = "SalesId,ReferenceNo,SalesDate,SalesPerson
                                               ,SalesSubs")] SalesMain salesmain)
{
   // do something with salesmain
}

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

【讨论】:

  • 感谢您的回答,我已经尝试过了,添加 contentType: 'application/json' 甚至不将数据传递给操作,在客户端出现错误The required anti-forgery form field "__RequestVerificationToken" is not present,它不是当我以您的方式传递数据时读取令牌字段,我删除了 [ValidateAntiForgery] 操作过滤器,然后检查它,SalesSubs 仍然是 null ,另外,我已经创建了一个附加 __RequestVerificationToken 的函数(更新了我的问题),因此,当我删除 contentType : "application/json" 和 JSON.stringify 时,它会附加,否则不会。
  • 那是因为你的 Bind 属性。请参阅我的更新答案。
  • 答案是这样,但我仍然设法创建了一个用于验证 JSON 防伪令牌的类,请查看 KEN Q 的答案here
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-03
  • 2021-02-14
  • 2014-07-04
  • 1970-01-01
  • 2017-02-06
  • 1970-01-01
相关资源
最近更新 更多