如何使用JSON将复杂类型传递给ASP.NET MVC控制器

33

我有一个视图,允许用户输入/编辑新Widget的数据。我想将数据组成JSON对象,并通过AJAX发送给我的控制器,以便在没有回发的情况下在服务器上进行验证。

我已经完成了所有工作,但我无法弄清楚如何传递数据,以便我的控制器方法可以接受复杂的Widget类型,而不是每个属性的单独参数。

所以,如果这是我的对象:

public class Widget
{
   public int Id { get; set; }
   public string Name { get; set; }
   public decimal Price { get; set; }
}

我希望我的控制方法看起来像这样:

public JsonResult Save(Widget widget)
{
   ...
}

目前,我的jQuery代码如下:

var formData = $("#Form1").serializeArray();

$.post("/Widget/Save",
   formData,
   function(result){}, "json");

我的表单(Form1)为Widget的每个属性(Id、Name和Price)都有一个输入字段。这很好用,但最终会将Widget的每个属性作为单独的参数传递给我的控制器方法。

我是否可以使用ActionFilterAttribute来“拦截”数据,并在调用我的控制器方法之前将其反序列化为Widget对象?

4个回答

26

谢谢Jeff,他让我走上了正确的道路。DefaultModelBinder已经足够聪明,可以为我完成所有的魔法……我的问题在于我的Widget类型定义。匆忙之中,我的类型被定义为:

public class Widget
{
   public int Id;
   public string Name;
   public decimal Price;
}

注意类型中使用了公共字段而不是公共属性。一旦我将它们改为属性,它就起作用了。下面是最终正常工作的源代码:

Widget.aspx:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Widget.aspx.cs" Inherits="MvcAjaxApp2.Views.Home.Widget" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <script src="../../Scripts/jquery-1.2.6.js" type="text/javascript"></script>   
    <script type="text/javascript"> 
    function SaveWidget()
    {
        var formData = $("#Form1").serializeArray();

        $.post("/Home/SaveWidget",
        formData,
        function(data){
            alert(data.Result);
        }, "json");
    }
    </script>
    <form id="Form1">
        <input type="hidden" name="widget.Id" value="1" />
        <input type="text" name="widget.Name" value="my widget" />
        <input type="text" name="widget.Price" value="5.43" />
        <input type="button" value="Save" onclick="SaveWidget()" />
    </form>
</asp:Content>

HomeController.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;

namespace MvcAjaxApp2.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewData["Title"] = "Home Page";
            ViewData["Message"] = "Welcome to ASP.NET MVC!";
            return View();
        }

        public ActionResult About()
        {
            ViewData["Title"] = "About Page";
            return View();
        }

        public ActionResult Widget()
        {
            ViewData["Title"] = "Widget";
            return View();
        }

        public JsonResult SaveWidget(Widget widget)
        {
            // Save the Widget
            return Json(new { Result = String.Format("Saved widget: '{0}' for ${1}", widget.Name, widget.Price) });
        }
    }
    public class Widget
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}

3
太棒了,很高兴你解决了这个问题。谢谢你分享如何修复它,这将有助于其他人在未来解决类似的问题! - Jeff Sheldon
1
公共字段在.NET中是魔鬼的产物。它们会破坏一切。我说的就是 EVERYTHING! - user1228
@MrBoJangles - 问题出在字段上,解决方案是将它们改为属性。 - MrDustpan
我在我的项目中使用了这种格式。它从未运行回调函数$.post(),其中我只添加了一个通用的alert('Successful');,不确定为什么它没有运行,但是它确实发布了我的数据,这让我很高兴,所以+1。当我在action中拥有method=post和我的控制器/更新时,不确定为什么我的简单$('#Form1').submit()不起作用。 - vapcguy
实际上,我发现如果我在回调函数的末尾去掉 , "json",警报就会起作用。这是给其他人的提示。 - vapcguy

6
请注意(在 MrDustpan 的解决方案中),MVC Action 方法中的参数 name widget 必须与 ASPX 文件中使用的 name 属性前缀匹配。
如果不是这样,则 Action 方法将始终接收一个 null 对象。
<input type="text" name="widget.Text" value="Hello" /> - OK
<input type="text" name="mywidget.Text" value="Hello" /> - FAILS

很好的观点,容易被忽视。似乎也是区分大小写的。 - The Matt

4

2
你需要做的是将JavaScript表单对象结构化,使其与后端对象结构相同。
{ Id : "id", Name : "name", Price : 1.0 }

然后使用toJSON插件将其转换为上述字符串。您将此字符串发送到后端,并使用类似JayRock库的东西将其转换为新的Widget对象。


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