¿Cómo publicar una matriz de objetos complejos con JSON, jQuery en ASP.NET MVC Controller?

92

Mi código actual se parece a lo siguiente. ¿Cómo puedo pasar mi matriz al controlador y qué tipo de parámetros debe aceptar la acción de mi controlador?

function getplaceholders() {
    var placeholders = $('.ui-sortable');
    var result = new Array();
    placeholders.each(function() {
        var ph = $(this).attr('id');
        var sections = $(this).find('.sort');
        var section;

        sections.each(function(i, item) {
            var sid = $(item).attr('id');

            result.push({ 'SectionId': sid, 'Placeholder': ph, 'Position': i });
        });
    });
    alert(result.toString());
    $.post(
        '/portal/Designer.mvc/SaveOrUpdate',
        result,
        function(data) {
            alert(data.Result);
        }, "json");
};

El método de acción de mi controlador parece

public JsonResult SaveOrUpdate(IList<PageDesignWidget> widgets)
JSC
fuente

Respuestas:

84

Encontré una solución. Utilizo una solución de Steve Gentile, jQuery y ASP.NET MVC: envío de JSON a una acción: revisado .

Mi código de vista ASP.NET MVC se ve así:

function getplaceholders() {
        var placeholders = $('.ui-sortable');
        var results = new Array();
        placeholders.each(function() {
            var ph = $(this).attr('id');
            var sections = $(this).find('.sort');
            var section;

            sections.each(function(i, item) {
                var sid = $(item).attr('id');
                var o = { 'SectionId': sid, 'Placeholder': ph, 'Position': i };
                results.push(o);
            });
        });
        var postData = { widgets: results };
        var widgets = results;
        $.ajax({
            url: '/portal/Designer.mvc/SaveOrUpdate',
            type: 'POST',
            dataType: 'json',
            data: $.toJSON(widgets),
            contentType: 'application/json; charset=utf-8',
            success: function(result) {
                alert(result.Result);
            }
        });
    };

y la acción de mi controlador está decorada con un atributo personalizado

[JsonFilter(Param = "widgets", JsonDataType = typeof(List<PageDesignWidget>))]
public JsonResult SaveOrUpdate(List<PageDesignWidget> widgets

El código para el atributo personalizado se puede encontrar aquí (el enlace ahora está roto).

Debido a que el enlace está roto, este es el código para JsonFilterAttribute

public class JsonFilter : ActionFilterAttribute
{
    public string Param { get; set; }
    public Type JsonDataType { get; set; }
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.HttpContext.Request.ContentType.Contains("application/json"))
        {
            string inputContent;
            using (var sr = new StreamReader(filterContext.HttpContext.Request.InputStream))
            {
                inputContent = sr.ReadToEnd();
            }
            var result = JsonConvert.DeserializeObject(inputContent, JsonDataType);
            filterContext.ActionParameters[Param] = result;
        }
    }
}

JsonConvert.DeserializeObject es de Json.NET

Enlace: serializar y deserializar JSON con Json.NET

JSC
fuente
Se ve muy bien: la publicación del blog y los enlaces del código de atributo personalizado ya no funcionan, ¿puede volver a publicar?
littlechris
4
Esta solución requiere cambios en el lado del cliente y del servidor. Sé que necesitabas esto hace mucho tiempo, pero también puedo proporcionar un enlace a un enfoque diferente, que usa un complemento jQuery simple que hace posible convertir cualquier objeto Javascript en un formulario que el enlazador de modelos predeterminado entiende y el modelo se une a los parámetros. No se necesitan filtros. erraticdev.blogspot.com/2010/12/… No sé cómo resolvió los errores de validación, pero también tengo una solución para eso: erraticdev.blogspot.com/2010/11/…
Robert Koritnik
3
¿Puede dar la fuente / origen de JavaScriptConvert.DeserializeObject?
Matthieu
Es la biblioteca Newtonsoft Json: si abre el administrador de paquetes nuget y busca en Newtonsoft, se mostrará (ahora que es 2016). Probablemente esto ahora sea obvio, pero en caso de que alguien se lo pregunte.
Robb Sadler
22

Filtros de acción, jquery stringify, bleh ...

Peter, esta funcionalidad es nativa de MVC. Esa es una de las cosas que hace que MVC sea tan grandioso.

$.post('SomeController/Batch', { 'ids': ['1', '2', '3']}, function (r) {
   ...
});

Y en la accion

[HttpPost]
public ActionResult Batch(string[] ids)
{
}

Funciona de maravilla:

ingrese la descripción de la imagen aquí

Si está usando jQuery 1.4+, entonces desea considerar la configuración del modo tradicional:

jQuery.ajaxSettings.traditional = true;

Como se describe aquí: http://www.dovetailsoftware.com/blogs/kmiller/archive/2010/02/24/jquery-1-4-breaks-asp-net-mvc-actions-with-array-parameters

Esto incluso funciona para objetos complejos. Si está interesado, debe consultar la documentación de MVC sobre Model Binding: http://msdn.microsoft.com/en-us/library/dd410405.aspx

Levitikon
fuente
1
Puede que tenga razón, pero el enlazador de modelos JSON es nuevo en MVC3 y la pregunta se hizo en 2008 cuando no se admitía. Vale la pena mencionarlo en su respuesta.
Piotr Owsiak
3
¿Cómo es este un ejemplo de pasar una matriz de objetos complejos ?
DuckMaestro
No lo es, pero el ejemplo sigue siendo válido (MVC 3+). Siempre que los nombres de sus parámetros coincidan con el modelo que espera, no tendrá ningún problema.
J. Mitchell
La clave aquí es crear un objeto JSON con el nombre del parámetro del método ("ids") y luego poner la matriz de objetos complejos dentro de ese. Además, haga que el tercer parámetro sea "verdadero" y se encargará del modo tradicional.
redwards510
11

En .NET4.5, MVC 5no hay necesidad de widgets.

Javascript:

objeto en JS: ingrese la descripción de la imagen aquí

mecanismo que hace publicar.

    $('.button-green-large').click(function() {
        $.ajax({
            url: 'Quote',
            type: "POST",
            dataType: "json",
            data: JSON.stringify(document.selectedProduct),
            contentType: 'application/json; charset=utf-8',
        });
    });

C#

Objetos:

public class WillsQuoteViewModel
{
    public string Product { get; set; }

    public List<ClaimedFee> ClaimedFees { get; set; }
}

public partial class ClaimedFee //Generated by EF6
{
    public long Id { get; set; }
    public long JourneyId { get; set; }
    public string Title { get; set; }
    public decimal Net { get; set; }
    public decimal Vat { get; set; }
    public string Type { get; set; }

    public virtual Journey Journey { get; set; }
}

Controlador:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Quote(WillsQuoteViewModel data)
{
....
}

Objeto recibido:

ingrese la descripción de la imagen aquí

Espero que esto te ahorre algo de tiempo.

Matas Vaitkevicius
fuente
8

Hacia la segunda mitad de Create REST API usando ASP.NET MVC que habla tanto JSON como XML simple , para citar:

Ahora necesitamos aceptar la carga útil JSON y XML, entregada a través de HTTP POST. A veces, es posible que su cliente desee cargar una colección de objetos de una sola vez para el procesamiento por lotes. Por lo tanto, pueden cargar objetos usando formato JSON o XML. No hay soporte nativo en ASP.NET MVC para analizar automáticamente JSON o XML publicado y mapear automáticamente los parámetros de acción. Entonces, escribí un filtro que lo hace ".

Luego implementa un filtro de acción que mapea el JSON a objetos C # con el código mostrado.

anónimo
fuente
Solo estaba escribiendo mi respuesta. Pero lo publicaré de todos modos ;-)
JSC
7

Primero descargue este código JavaScript, JSON2.js , que nos ayudará a serializar el objeto en una cadena.

En mi ejemplo, estoy publicando las filas de un jqGrid a través de Ajax:

    var commissions = new Array();
    // Do several row data and do some push. In this example is just one push.
    var rowData = $(GRID_AGENTS).getRowData(ids[i]);
    commissions.push(rowData);
    $.ajax({
        type: "POST",
        traditional: true,
        url: '<%= Url.Content("~/") %>' + AREA + CONTROLLER + 'SubmitCommissions',
        async: true,
        data: JSON.stringify(commissions),
        dataType: "json",
        contentType: 'application/json; charset=utf-8',
        success: function (data) {
            if (data.Result) {
                jQuery(GRID_AGENTS).trigger('reloadGrid');
            }
            else {
                jAlert("A problem ocurred during updating", "Commissions Report");
            }
        }
    });

Ahora en el controlador:

    [HttpPost]
    [JsonFilter(Param = "commissions", JsonDataType = typeof(List<CommissionsJs>))]
    public ActionResult SubmitCommissions(List<CommissionsJs> commissions)
    {
        var result = dosomething(commissions);
        var jsonData = new
        {
            Result = true,
            Message = "Success"
        };
        if (result < 1)
        {
            jsonData = new
            {
                Result = false,
                Message = "Problem"
            };
        }
        return Json(jsonData);
    }

Cree una clase JsonFilter (gracias a la referencia JSC).

    public class JsonFilter : ActionFilterAttribute
    {
        public string Param { get; set; }
        public Type JsonDataType { get; set; }
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext.HttpContext.Request.ContentType.Contains("application/json"))
            {
                string inputContent;
                using (var sr = new StreamReader(filterContext.HttpContext.Request.InputStream))
                {
                    inputContent = sr.ReadToEnd();
                }
                var result = JsonConvert.DeserializeObject(inputContent, JsonDataType);
                filterContext.ActionParameters[Param] = result;
            }
        }
    }

Cree otra clase para que el filtro pueda analizar la cadena JSON hasta el objeto manipulable real: esta clase comissionsJS son todas las filas de mi jqGrid.

    public class CommissionsJs
    {
        public string Amount { get; set; }

        public string CheckNumber { get; set; }

        public string Contract { get; set; }
        public string DatePayed { get; set; }
        public string DealerName { get; set; }
        public string ID { get; set; }
        public string IdAgentPayment { get; set; }
        public string Notes { get; set; }
        public string PaymentMethodName { get; set; }
        public string RowNumber { get; set; }
        public string AgentId { get; set; }
    }

Espero que este ejemplo ayude a ilustrar cómo publicar un objeto complejo.

Sanchitos
fuente
0

Oh Dios mío. No es necesario hacer nada especial. solo en tu sección de publicaciones haz lo siguiente:

    $.post(yourURL,{ '': results})(function(e){ ...}

En el servidor usa esto:

   public ActionResult MethodName(List<yourViewModel> model){...}

este enlace te ayudará a hacerlo ...

Mahdi Moghimi
fuente
-1
    [HttpPost]
    public bool parseAllDocs([FromBody] IList<docObject> data)
    {
        // do stuff

    }
JsonW
fuente