将JSON数据发布到ASP.NET MVC

Jim*_*mbo 57 c# ajax asp.net-mvc jquery json

我试图使用JSON将一个行项目列表添加到网页,然后使用相同的JSON结构(除了更改了字段值之外)通过ajax请求操作并将其发送回服务器.

从服务器接收数据很容易,操作更容易!但是将JSON数据发送回服务器以保存......自杀时间!请有人帮忙!

使用Javascript

var lineitems;

// get data from server
$.ajax({
    url: '/Controller/GetData/',
    success: function(data){
        lineitems = data;
    }
});

// post data to server
$.ajax({
    url: '/Controller/SaveData/',
    data: { incoming: lineitems }
});
Run Code Online (Sandbox Code Playgroud)

C# - 对象

public class LineItem{
    public string reference;
    public int quantity;
    public decimal amount;
}
Run Code Online (Sandbox Code Playgroud)

C# - 控制器

public JsonResult GetData()
{
    IEnumerable<LineItem> lineItems = ... ; // a whole bunch of line items
    return Json(lineItems);
}

public JsonResult SaveData(IEnumerable<LineItem> incoming){
    foreach(LineItem item in incoming){
        // save some stuff
    }
    return Json(new { success = true, message = "Some message" });
}
Run Code Online (Sandbox Code Playgroud)

数据作为序列化的发布数据到达服务器.自动模型绑定器尝试绑定IEnumerable<LineItem> incoming并且令人惊讶地得到IEnumerable具有正确数量的结果LineItems- 它不会用数据填充它们.

使用来自多个来源的答案,主要是djch在另一个stackoverflow post及BeRecursive以下,我使用两个主要方法解决了我的问题.

服务器端

下面的解串器需​​要参考System.Runtime.Serializationusing System.Runtime.Serialization.Json

    private T Deserialise<T>(string json)
    {
        using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
        {
            var serialiser = new DataContractJsonSerializer(typeof(T));
            return (T)serialiser.ReadObject(ms);
        }
    }

    public void Action(int id, string items){
        IEnumerable<LineItem> lineitems = Deserialise<IEnumerable<LineItem>>(items);
        // do whatever needs to be done - create, update, delete etc.
    }
Run Code Online (Sandbox Code Playgroud)

客户端

它使用json.org的stringify方法,可在此依赖https://github.com/douglascrockford/JSON-js/blob/master/json2.js(缩小时为2.5kb)中提供

        $.ajax({
            type: 'POST',
            url: '/Controller/Action',
            data: { 'items': JSON.stringify(lineItems), 'id': documentId }
        });
Run Code Online (Sandbox Code Playgroud)

BeR*_*ive 31

看看Phil Haack关于模型绑定JSON数据的帖子.问题是默认模型绑定器没有正确序列化JSON.您需要某种ValueProvider或者您可以编写自定义模型绑定器:

using System.IO;
using System.Web.Script.Serialization;

public class JsonModelBinder : DefaultModelBinder {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
            if(!IsJSONRequest(controllerContext)) {
                return base.BindModel(controllerContext, bindingContext);
            }

            // Get the JSON data that's been posted
            var request = controllerContext.HttpContext.Request;
            //in some setups there is something that already reads the input stream if content type = 'application/json', so seek to the begining
            request.InputStream.Seek(0, SeekOrigin.Begin);
            var jsonStringData = new StreamReader(request.InputStream).ReadToEnd();

            // Use the built-in serializer to do the work for us
            return new JavaScriptSerializer()
                .Deserialize(jsonStringData, bindingContext.ModelMetadata.ModelType);

            // -- REQUIRES .NET4
            // If you want to use the .NET4 version of this, change the target framework and uncomment the line below
            // and comment out the above return statement
            //return new JavaScriptSerializer().Deserialize(jsonStringData, bindingContext.ModelMetadata.ModelType);
        }

        private static bool IsJSONRequest(ControllerContext controllerContext) {
            var contentType = controllerContext.HttpContext.Request.ContentType;
            return contentType.Contains("application/json");
        }
    }

public static class JavaScriptSerializerExt {
        public static object Deserialize(this JavaScriptSerializer serializer, string input, Type objType) {
            var deserializerMethod = serializer.GetType().GetMethod("Deserialize", BindingFlags.NonPublic | BindingFlags.Static);

            // internal static method to do the work for us
            //Deserialize(this, input, null, this.RecursionLimit);

            return deserializerMethod.Invoke(serializer,
                new object[] { serializer, input, objType, serializer.RecursionLimit });
        }
    }
Run Code Online (Sandbox Code Playgroud)

并告诉MVC在Global.asax文件中使用它:

ModelBinders.Binders.DefaultBinder = new JsonModelBinder();
Run Code Online (Sandbox Code Playgroud)

此外,此代码使用内容类型='application/json',因此请确保在jquery中设置如下:

$.ajax({
    dataType: "json",
    contentType: "application/json",            
    type: 'POST',
    url: '/Controller/Action',
    data: { 'items': JSON.stringify(lineItems), 'id': documentId }
});
Run Code Online (Sandbox Code Playgroud)

  • @Jimbo我对请求有同样的问题.这不是jquery的错,你的应用程序(和我的应用程序)中的其他内容正在读取输入流,如果它是'application/json'类型.这就是输入流看起来空的原因.我修改了代码以在输入流上调用seek,现在它很开心. (2认同)

Rob*_*nik 13

这样做最简单的方法

我恳请您阅读这篇直接解决您问题的博文.

使用自定义模型绑定器并不是真正明智的,因为Phil Haack指出(他的博客帖子也链接在上面的博客文章中).

基本上你有三个选择:

  1. 编写JsonValueProviderFactory并使用客户端库,json2.js以便直接与JSON通信.

  2. 写一个JQueryValueProviderFactory了解在$.ajax或中发生的jQuery JSON对象转换

  3. 使用博客文章中概述的非常简单快速的jQuery插件,准备任何JSON对象(即使是将被绑定的数组IList<T>和将在服务器端正确解析的日期作为DateTime实例),Asp.net MVC将会理解这一点模型活页夹.

在所有三个中,最后一个是最简单的并且不会干扰Asp.net MVC内部工作,从而降低可能的bug表面.使用博客文章中概述的这种技术将正确地绑定您的强类型操作参数并验证它们.所以这基本上是双赢的局面.

  • 太棒了,这个插件正是我所寻找的,并且完美无缺!谢谢! (2认同)

Ken*_*son 9

在MVC3中,他们已经添加了这个.

但是更好的是,由于MVC源代码是开放的,你可以获取ValueProvider并在你自己的代码中自己使用它(如果你还没有在MVC3上).

你最终会得到这样的东西

ValueProviderFactories.Factories.Add(new JsonValueProviderFactory())
Run Code Online (Sandbox Code Playgroud)

  • 是的,我的意思是,因为MVC3(以及MVC的所有版本)都是开源的,你可以获取源代码并复制粘贴到JsonValueProviderFactory上 (2认同)