Como postar uma matriz de objetos complexos com JSON, jQuery para ASP.NET MVC Controller?

92

Meu código atual se parece com o seguinte. Como posso passar meu array para o controlador e que tipo de parâmetros minha ação do controlador deve aceitar?

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

Meu método de ação do controlador parece

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

Respostas:

84

Eu encontrei uma solução. Eu uso uma solução de Steve Gentile, jQuery e ASP.NET MVC - enviando JSON para um Action - Revisited .

Meu código de visualização ASP.NET MVC se parece com:

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

e minha ação do controlador é decorada com um atributo personalizado

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

O código para o atributo personalizado pode ser encontrado aqui (o link está quebrado agora).

Como o link está quebrado, este é o código para o 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 é de Json.NET

Link: serializando e desserializando JSON com Json.NET

JSC
fonte
Parece ótimo - a postagem do blog e os links do código de atributo personalizado não funcionam mais - você pode postar novamente?
littlechris
4
Esta solução requer mudanças no lado do cliente e do servidor. Eu sei que você precisava disso há muito tempo, mas também posso fornecer um link para uma abordagem diferente, que usa um plug-in jQuery simples que torna possível converter qualquer objeto Javascript em uma forma que o binder de modelo padrão entenda e o modelo se vincule a parâmetros. Sem filtros necessários. erraticdev.blogspot.com/2010/12/… Não sei como você resolveu os erros de validação, mas também tenho uma solução para isso: erraticdev.blogspot.com/2010/11/…
Robert Koritnik
3
Você pode fornecer a fonte / origem de JavaScriptConvert.DeserializeObject?
Matthieu
É a biblioteca Newtonsoft Json - se você abrir o gerenciador de pacotes nuget e pesquisar no Newtonsoft, ele aparecerá para você (agora que é 2016). Provavelmente agora é óbvio, mas caso alguém esteja se perguntando.
Robb Sadler de
22

Filtros de ação, jquery stringify, bleh ...

Peter, essa funcionalidade é nativa do MVC. Essa é uma das coisas que torna o MVC tão bom.

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

E na ação,

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

Funciona como um encanto:

insira a descrição da imagem aqui

Se você estiver usando jQuery 1.4+, então você deseja configurar o modo tradicional:

jQuery.ajaxSettings.traditional = true;

Conforme descrito aqui: http://www.dovetailsoftware.com/blogs/kmiller/archive/2010/02/24/jquery-1-4-breaks-asp-net-mvc-actions-with-array-parameters

Isso funciona até mesmo para objetos complexos. Se estiver interessado, você deve consultar a documentação MVC sobre Model Binding: http://msdn.microsoft.com/en-us/library/dd410405.aspx

Levitikon
fonte
1
você pode estar certo, mas o fichário de modelo JSON é novo no MVC3 e a pergunta foi feita em 2008, quando não era compatível. Vale a pena mencionar em sua resposta.
Piotr Owsiak
3
Como este é um exemplo de transmissão de uma matriz de objetos complexos ?
DuckMaestro
Não é, mas o exemplo ainda se aplica (MVC 3+). Contanto que os nomes dos seus parâmetros correspondam ao modelo que você espera, você não terá nenhum problema.
J. Mitchell
A chave aqui é criar um objeto JSON com o nome do parâmetro do método ("ids") e, em seguida, colocar a matriz de objetos complexos dentro dele. Além disso, torne o terceiro parâmetro "verdadeiro" e você cuidará do modo tradicional.
redwards510
11

Em .NET4.5, MVC 5não há necessidade de widgets.

Javascript:

objeto em JS: insira a descrição da imagem aqui

mecanismo que posta.

    $('.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 recebido:

insira a descrição da imagem aqui

Espero que isso economize algum tempo.

Matas Vaitkevicius
fonte
8

Em direção à segunda metade de Criar API REST usando ASP.NET MVC que fala JSON e XML simples , para citar:

Agora precisamos aceitar a carga útil JSON e XML, entregue via HTTP POST. Às vezes, seu cliente pode querer fazer upload de uma coleção de objetos de uma só vez para processamento em lote. Portanto, eles podem fazer upload de objetos usando o formato JSON ou XML. Não há suporte nativo na ASP.NET MVC para analisar automaticamente JSON ou XML postado e mapear automaticamente para parâmetros de ação. Então, eu escrevi um filtro que faz isso. "

Ele então implementa um filtro de ação que mapeia o JSON para objetos C # com o código mostrado.

anônimo
fonte
Eu estava apenas escrevendo minha resposta. Mas vou postar de qualquer maneira ;-)
JSC
7

Primeiro baixe este código JavaScript, JSON2.js , que nos ajudará a serializar o objeto em uma string.

No meu exemplo, estou postando as linhas de um jqGrid via 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");
            }
        }
    });

Agora no 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);
    }

Crie uma classe JsonFilter (graças à referência 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;
            }
        }
    }

Crie outra classe para que o filtro possa analisar a string JSON para o objeto manipulável real: Esta classe comissionsJS são todas as linhas do meu 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 exemplo ajude a ilustrar como postar um objeto complexo.

Sanchitos
fonte
0

Oh meu Deus. não precisa fazer nada de especial. apenas na sua seção de postagem, faça o seguinte:

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

No servidor, use isto:

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

este link ajuda você a fazer ...

mahdi moghimi
fonte
-1
    [HttpPost]
    public bool parseAllDocs([FromBody] IList<docObject> data)
    {
        // do stuff

    }
JsonW
fonte