C# 将JSON数据发布到ASP.NET MVC

C# 将JSON数据发布到ASP.NET MVC,c#,jquery,asp.net-mvc,ajax,json,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){ li

我正在尝试使用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" });
}
客户端

它使用json.org的stringify方法,在这个dependecy中可用(缩小时为2.5kb)


看看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();
此外,这段代码使用了content type='application/json',因此请确保在jquery中这样设置:

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

在MVC3中,他们添加了这个

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

你最终会得到这样的结果

ValueProviderFactories.Factories.Add(new JsonValueProviderFactory())

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

然后我会使用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"
    }
}
}
这是最简单的方法 我建议您阅读直接解决您问题的内容。

正如Phil Haack指出的那样,使用自定义模型绑定并不是真正明智的做法(他的博客文章也链接在上面的博客文章中)

基本上,您有三个选项:

  • 编写一个
    JsonValueProviderFactory
    并使用客户端库,如
    json2.js
    与JSON直接通信

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

  • 使用博客文章中介绍的非常简单快速的jQuery插件,它可以准备任何JSON对象(甚至是绑定到
    IList
    日期的数组,它们将在服务器端正确解析为
    DateTime
    实例)Asp.net MVC默认模型绑定器将理解这一点

  • 在这三种方法中,最后一种是最简单的,不会干扰Asp.NETMVC内部工作,从而降低了可能出现的错误。使用博客文章中概述的这项技术将正确地绑定强类型操作参数并验证它们。因此,这基本上是一个双赢的局面。

    是我使用的,这样我们就可以在Json.Net上进行标准化(我们有MVC5和WebApi 5——WebApi 5已经使用Json.Net),但我发现了一个问题。当您要发布的路由中有参数时,MVC会尝试调用URI值的模型绑定器,而这段代码将尝试将发布的JSON绑定到这些值

    例如:

    [HttpPost]
    [Route("Customer/{customerId:int}/Vehicle/{vehicleId:int}/Policy/Create"]
    public async Task<JsonNetResult> Create(int customerId, int vehicleId, PolicyRequest policyRequest)
    
    幸运的是,ValueProvider在使用此方法时已计算出路由值。

    您可以尝试这些。 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});
    
        }
    
    [HttpPost]
    公共JsonResult操作(字符串数据){
    string\u jsonObject=data.Replace(@“\”,string.Empty);
    var serializer=new System.Web.Script.Serialization.JavaScriptSerializer();
    Dictionary jsonObject=序列化程序。反序列化(_jsonObject);
    返回Json(新对象{status=true});
    }
    
    我按照vestigal的提示解决了这个问题:

    当我需要将大型json发布到控制器中的操作时,我会遇到著名的“使用json JavaScriptSerializer进行反序列化时出错。字符串长度超过maxJsonLength属性上设置的值。\r\n参数名称:输入值提供程序”

    我所做的是创建一个新的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;
            }
        }
    }
    
    我使用“手动”反序列化解决了这个问题。 我会用代码解释的

    public ActionResult MyMethod([System.Web.Http.FromBody] MyModel model)
    {
     if (module.Fields == null && !string.IsNullOrEmpty(Request.Form["fields"]))
     {
       model.Fields = JsonConvert.DeserializeObject<MyFieldModel[]>(Request.Form["fields"]);
     }
     //... more code
    }
    
    public ActionResult MyMethod([System.Web.Http.FromBody]MyModel)
    {
    if(module.Fields==null&&!string.IsNullOrEmpty(Request.Form[“Fields”]))
    {
    model.Fields=JsonConvert.DeserializeObject(Request.Form[“Fields”]);
    }
    //…更多代码
    }
    
    我看到这里的每个人都“走了很长的路!”。只要你使用的是
    MVC
    ,我强烈建议你使用最简单的方法,那就是
    Newtonsoft.JSON
    …如果你不想使用库,也可以查看下面的答案链接。我花了很长的时间为自己解决了这个问题,这些就是我找到的解决方案

    首先实现Newtonsoft.Json:

    using Newtonsoft.Json;
    
    $.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" });
    }
    
    准备Ajax请求:

    using Newtonsoft.Json;
    
    $.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" });
    }
    
    然后进入结果类:

    using Newtonsoft.Json;
    
    $.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" });
    }
    
    public ActionResult SaveData(字符串传入,int documentId){
    //作为模型数组反序列化到模型中
    LineItem[]jsr=JsonConvert.DeserializeObject(临时);
    foreach(jsr中的行项目){
    //留点东西
    }
    返回Json(新的{success=true,message=“Some message”});
    }
    
    看到上面的诀窍了吗?我没有使用
    JsonResult
    而是使用常规
    ActionResult
    和包含json字符串的字符串。然后反序列化到我的
    模型中
    ,这样我就可以在我拥有的任何操作方法中使用它

    此方法的优点是:

    using Newtonsoft.Json;
    
    $.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" });
    }
    
  • 更容易在动作之间传递
  • 代码的使用越来越少,越来越清晰