将JSON数据发布到ASP.NET MVC

时间:2010-11-12 11:25:58

标签: c# jquery asp.net-mvc ajax json

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

从服务器接收数据很简单,操作更容易!但是将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 }
});

C# - 对象

public class LineItem{
    public string reference;
    public int quantity;
    public decimal amount;
}

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" });
}

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

使用来自多个来源的答案,主要是djch在另一个stackoverflow帖子和下面的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.
    }

客户端

它使用了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 }
        });

9 个答案:

答案 0 :(得分:29)

model binding JSON data上看看Phil Haack的帖子。问题是默认模型绑定器没有正确序列化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 });
        }
    }

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

ModelBinders.Binders.DefaultBinder = new JsonModelBinder();

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

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

答案 1 :(得分:12)

最简单的方法

我建议您阅读直接解决问题的this blog post

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

基本上你有三个选项:

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

  2. 编写一个JQueryValueProviderFactory,了解$.ajax

  3. 中发生的jQuery JSON对象转换
  4. 使用博客文章中概述的非常简单快速的jQuery插件,准备任何JSON对象(甚至是数组,它将绑定到IList<T>日期,它将在服务器端正确解析为DateTime个实例),这些实例将被Asp.net MVC默认模型绑定器理解。

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

答案 2 :(得分:8)

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

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

你最终会得到类似的东西

ValueProviderFactories.Factories.Add(new JsonValueProviderFactory())

答案 3 :(得分:3)

我在这里提到了这个问题:

Can I set an unlimited length for maxJsonLength in web.config?

当我需要将大型json发布到控制器中的动作时,我会在使用JSON JavaScriptSerializer进行反序列化时获得着名的“错误。”字符串的长度超过了maxJsonLength属性上设置的值。\ r \ nParameter name:输入值提供者“。

我所做的是创建一个新的ValueProviderFactory,LargeJsonValueProviderFactory,并在GetDeserializedObject方法中设置MaxJsonLength = Int32.MaxValue

public sealed class LargeJsonValueProviderFactory : ValueProviderFactory
{
    private static void AddToBackingStore(LargeJsonValueProviderFactory.EntryLimitedDictionary backingStore, string prefix, object value)
    {
        IDictionary<string, object> dictionary = value as IDictionary<string, object>;
        if (dictionary != null)
        {
            foreach (KeyValuePair<string, object> keyValuePair in (IEnumerable<KeyValuePair<string, object>>) dictionary)
                LargeJsonValueProviderFactory.AddToBackingStore(backingStore, LargeJsonValueProviderFactory.MakePropertyKey(prefix, keyValuePair.Key), keyValuePair.Value);
        }
        else
        {
            IList list = value as IList;
            if (list != null)
            {
                for (int index = 0; index < list.Count; ++index)
                    LargeJsonValueProviderFactory.AddToBackingStore(backingStore, LargeJsonValueProviderFactory.MakeArrayKey(prefix, index), list[index]);
            }
            else
                backingStore.Add(prefix, value);
        }
    }

    private static object GetDeserializedObject(ControllerContext controllerContext)
    {
        if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            return (object) null;
        string end = new StreamReader(controllerContext.HttpContext.Request.InputStream).ReadToEnd();
        if (string.IsNullOrEmpty(end))
            return (object) null;

        var serializer = new JavaScriptSerializer {MaxJsonLength = Int32.MaxValue};

        return serializer.DeserializeObject(end);
    }

    /// <summary>Returns a JSON value-provider object for the specified controller context.</summary>
    /// <returns>A JSON value-provider object for the specified controller context.</returns>
    /// <param name="controllerContext">The controller context.</param>
    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        if (controllerContext == null)
            throw new ArgumentNullException("controllerContext");
        object deserializedObject = LargeJsonValueProviderFactory.GetDeserializedObject(controllerContext);
        if (deserializedObject == null)
            return (IValueProvider) null;
        Dictionary<string, object> dictionary = new Dictionary<string, object>((IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase);
        LargeJsonValueProviderFactory.AddToBackingStore(new LargeJsonValueProviderFactory.EntryLimitedDictionary((IDictionary<string, object>) dictionary), string.Empty, deserializedObject);
        return (IValueProvider) new DictionaryValueProvider<object>((IDictionary<string, object>) dictionary, CultureInfo.CurrentCulture);
    }

    private static string MakeArrayKey(string prefix, int index)
    {
        return prefix + "[" + index.ToString((IFormatProvider) CultureInfo.InvariantCulture) + "]";
    }

    private static string MakePropertyKey(string prefix, string propertyName)
    {
        if (!string.IsNullOrEmpty(prefix))
            return prefix + "." + propertyName;
        return propertyName;
    }

    private class EntryLimitedDictionary
    {
        private static int _maximumDepth = LargeJsonValueProviderFactory.EntryLimitedDictionary.GetMaximumDepth();
        private readonly IDictionary<string, object> _innerDictionary;
        private int _itemCount;

        public EntryLimitedDictionary(IDictionary<string, object> innerDictionary)
        {
            this._innerDictionary = innerDictionary;
        }

        public void Add(string key, object value)
        {
            if (++this._itemCount > LargeJsonValueProviderFactory.EntryLimitedDictionary._maximumDepth)
                throw new InvalidOperationException("JsonValueProviderFactory_RequestTooLarge");
            this._innerDictionary.Add(key, value);
        }

        private static int GetMaximumDepth()
        {
            NameValueCollection appSettings = ConfigurationManager.AppSettings;
            if (appSettings != null)
            {
                string[] values = appSettings.GetValues("aspnet:MaxJsonDeserializerMembers");
                int result;
                if (values != null && values.Length > 0 && int.TryParse(values[0], out result))
                    return result;
            }
            return 1000;
        }
    }
}

然后,在Global.asax.cs的Application_Start方法中,将ValueProviderFactory替换为新的:

protected void Application_Start()
    {
        ...

        //Add LargeJsonValueProviderFactory
        ValueProviderFactory jsonFactory = null;
        foreach (var factory in ValueProviderFactories.Factories)
        {
            if (factory.GetType().FullName == "System.Web.Mvc.JsonValueProviderFactory")
            {
                jsonFactory = factory;
                break;
            }
        }

        if (jsonFactory != null)
        {
            ValueProviderFactories.Factories.Remove(jsonFactory);
        }

        var largeJsonValueProviderFactory = new LargeJsonValueProviderFactory();
        ValueProviderFactories.Factories.Add(largeJsonValueProviderFactory);
    }

答案 4 :(得分:2)

你可以尝试这些。 1.在通过ajax调用服务器操作之前,对您的JSON对象进行字符串化 2.反序列化操作中的字符串,然后将数据用作字典。

下面的Javascript示例(发送JSON对象

$.ajax(
   {
       type: 'POST',
       url: 'TheAction',
       data: { 'data': JSON.stringify(theJSONObject) 
   }
})

下面的行动(C#)样本

[HttpPost]
public JsonResult TheAction(string data) {

       string _jsonObject = data.Replace(@"\", string.Empty);
       var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();           
        Dictionary<string, string> jsonObject = serializer.Deserialize<Dictionary<string, string>>(_jsonObject);


        return Json(new object{status = true});

    }

答案 5 :(得分:0)

如果您已将JSON数据作为字符串输入(例如'[{“id”:1,“name”:“Charles”},{“id”:8,“name”:“John” },{ “ID”:13, “姓名”: “萨利”}]')

然后我使用JSON.net并使用Linq到JSON来获取值...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

    if (Request["items"] != null)
    {
        var items = Request["items"].ToString(); // Get the JSON string
        JArray o = JArray.Parse(items); // It is an array so parse into a JArray
        var a = o.SelectToken("[0].name").ToString(); // Get the name value of the 1st object in the array
        // a == "Charles"
    }
}
}

答案 6 :(得分:0)

BeRecursive's answer是我使用的,所以我们可以在Json.Net上标准化(我们有MVC5和WebApi 5 - WebApi 5已经使用Json.Net),但我发现了一个问题。当您在路径中有参数进行POST时,MVC会尝试为模型绑定器调用URI值,此代码将尝试将发布的JSON绑定到这些值。

示例:

[HttpPost]
[Route("Customer/{customerId:int}/Vehicle/{vehicleId:int}/Policy/Create"]
public async Task<JsonNetResult> Create(int customerId, int vehicleId, PolicyRequest policyRequest)

BindModel函数被调用三次,第一次被轰炸,因为它试图将JSON绑定到customerId并出现错误:Error reading integer. Unexpected token: StartObject. Path '', line 1, position 1.

我将此代码块添加到BindModel的顶部:

if (bindingContext.ValueProvider.GetValue(bindingContext.ModelName) != null) {
    return base.BindModel(controllerContext, bindingContext);
}

幸运的是,ValueProvider在获取此方法时会计算出路由值。

答案 7 :(得分:0)

我使用“手动”反序列化解决了问题。 我将在代码中解释

<div ng-repeat="point in points">                                
   <input type="text" ng-model="point.oy"  onchange="angular.element(this).scope().changeLocation(point.name)">
   <input type="text" ng-model="point.ox"  onchange="angular.element(this).scope().changeLocation(point.name)">
   <!--.... other inputs -->
</div>

答案 8 :(得分:0)

我在这里看到所有人“走了很长的路!” 。只要您使用MVC,我强烈建议您对Newtonsoft.JSON全部使用最简单的方法...另外,如果您不想使用库,请查看下面的答案链接。为此,我花了很多时间研究自己的问题,这些是我找到的解决方案;

首先实施Newtonsoft.Json:

using Newtonsoft.Json;

准备您的Ajax请求:

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

然后参加结果课程:

public ActionResult SaveData(string incoming, int documentId){
    // DeSerialize into your Model as your Model Array
    LineItem[] jsr = JsonConvert.DeserializeObject<LineItem[]>(Temp);
    foreach(LineItem item in jsr){
        // save some stuff
    }
    return Json(new { success = true, message = "Some message" });
}

看到那里的把戏了吗?我没有使用JsonResult,而是将常规ActionResult与包含json字符串的字符串一起使用。然后反序列化到我的Model中,以便可以在我拥有的任何操作方法中使用它。

此方法的优点是:

  1. 更容易在动作之间传递,
  2. 更简洁的代码用法
  3. 无需更改模型,
  4. 使用JSON.stringify(Model)的简单实现
  5. 仅传递字符串

此方法的缺点是:

  1. 仅传递字符串
  2. 反序列化过程

还要检查这些确实有用的问题和答案:

https://stackoverflow.com/a/45682516/861019

另一种方法:

https://stackoverflow.com/a/31656160/861019

和另一种方法:

https://stackoverflow.com/a/50787450/861019