web-dev-qa-db-fra.com

Publier un tableau d'objets via JSON sur ASP.Net MVC3

Je cherche une solution pour POSTER un tableau d'objets sur MVC3 via JSON.

Exemple de code sur lequel je travaille: http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx =

JS:

var data = { ItemList: [ {Str: 'hi', Enabled: true} ], X: 1, Y: 2 };

$.ajax({
    url: '/list/save',
    data: JSON.stringify(data),
    success: success,
    error: error,
    type: 'POST',
    contentType: 'application/json, charset=utf-8',
    dataType: 'json'
});

ListViewModel.cs:

public class ListViewModel
{
    public List<ItemViewModel> ItemList { get; set; }
    public float X { get; set; }
    public float Y { get; set; }
}

ItemViewModel.cs:

public class ItemViewModel
{
    public string Str;   // originally posted with: { get; set; }
    public bool Enabled; // originally posted with: { get; set; }
}

ListController.cs:

public ActionResult Save(ListViewModel list)
{
    // Do something
}

Le résultat de ce POST:

est définie, sur un ListViewModel
Ses propriétés X et Y sont définies
La propriété ItemList sous-jacente est définie
L'ItemList contient un élément, comme il se doit
L'élément de cette ItemList n'est pas initialisé. Str est nul et Enabled est faux.

Autrement dit, voici ce que j'obtiens de la liaison de modèle de MVC3:

list.X == 1
list.Y == 2
list.ItemList != null
list.ItemList.Count == 1
list.ItemList[0] != null
list.ItemList[0].Str == null

Il semblerait que le MVC3 JsonValueProvider ne fonctionne pas pour les objets complexes. Comment puis-je faire fonctionner cela? Dois-je modifier le MVC3 JsonValueProvider existant et le corriger? Si oui, comment puis-je y accéder et le remplacer dans un projet MVC3?

Questions StackOverflow connexes que j'ai déjà posées en vain:

Asp.net Mvc Ajax Json (post Array) Utilise MVC2 et un ancien codage basé sur les formulaires - cette approche échoue avec un objet qui contient un tableau d'objets (JQuery ne parvient pas à l'encoder correctement).

Publier un tableau d'objets complexes avec JSON, JQuery sur ASP.NET MVC Controller Utilise un hack que j'aimerais éviter à la place où le Controller reçoit à la place une chaîne simple qu'il désérialise ensuite manuellement, plutôt que de tirer parti le cadre.

MVC3 RC2 JSON Post Binding ne fonctionne pas correctement N'avait pas son type de contenu défini - il est défini dans mon code.

Comment publier un tableau d'objets complexes avec JSON, jQuery sur ASP.NET MVC Controller? Ce pauvre gars a dû écrire un JsonFilter juste pour analyser un tableau. Un autre hack que je préfère éviter.

Alors, comment puis-je y arriver?

65
Chris Moschini

Le problème était que les propriétés des modèles qui figuraient dans la liste n'avaient pas obtenu/défini sur leurs propriétés publiques. Autrement dit, la liaison JSON automatique de MVC3 ne fonctionne que sur les propriétés d'objet qui ont get et set.

Cela ne liera pas:

public string Str;

Cela liera:

public string Str { get; set; }
30
Chris Moschini

En plus de { get; set; }, voici quelques-unes des conditions de prise en charge de la liaison JSON:

  1. Il s'agit d'une nouvelle fonctionnalité d'ASP.NET MVC 3 (voir " JavaScript et AJAX Improvements ")).
  2. Les chaînes de l'objet JSON ("X", "Y", "Str" et "Activé") doivent correspondre aux propriétés de l'objet ViewModel.
  3. Les propriétés de l'objet ViewModel doivent avoir { get; set; } méthode.
  4. Vous devez spécifier le type de contenu comme "application/json" dans la demande.
  5. Si cela ne fonctionne toujours pas, vérifiez la chaîne JSON pour vous assurer qu'elle est valide.

En savoir plus sur mon message .

J'espère que ça t'as aidé!

43
stack247

C'est étrange. Je n'arrive pas à reproduire votre comportement. Voici ma configuration (ASP.NET MVC 3 RTM):

Modèle:

public class ItemViewModel
{
    public string Str { get; set; }
    public bool Enabled { get; set; }
}

public class ListViewModel
{
    public List<ItemViewModel> ItemList { get; set; }
    public float X { get; set; }
    public float Y { get; set; }
}

Manette:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Save(ListViewModel list)
    {
        return Json(list);
    }
}

Vue:

@{
    ViewBag.Title = "Home Page";
}

<script type="text/javascript">
    $(function () {
        var data = { ItemList: [{ Str: 'hi', Enabled: true}], X: 1, Y: 2 };

        $.ajax({
            url: '@Url.Action("save", "home")',
            data: JSON.stringify(data),
            type: 'POST',
            contentType: 'application/json',
            dataType: 'json',
            success: function (result) {
                alert(result.ItemList[0].Str);
            }
        });
    });
</script>

Exécution de ces alertes "hi" et à l'intérieur de l'action Save tout est correctement initialisé.

Et pour mémoire, ce qui ne fonctionne pas, ce sont les dictionnaires. J'ai ouvert un ticket à propos du problème.

29
Darin Dimitrov

J'ai eu un problème similaire et j'ai constaté que pour un objet complexe, les valeurs numériques étaient manquantes. Ils entraient comme des zéros. c'est à dire.

    var person = {
        Name: "john",
        Age: 9
    }

était reçu par le contrôleur MVC en tant qu'objet Personne où les propriétés étaient remplies comme Name=John et Age=0.

J'ai ensuite fait de la valeur Age en Javascript une chaîne ... c'est-à-dire.

    var person = {
        Name: "john",
        Age: "9"
    }

Et cela s'est bien passé ...

3
Dev

C'est parce que les liants MVC sont un peu nul. Cependant, ils fonctionnent assez bien si toutes les valeurs JSON arrivent sous forme de chaîne.

Dans JS si vous faites cela

var myObject = {thisNumber:1.6};

myObject.thisNumber=myObject.thisNumber-.6;

Il évaluera à 1 et non à 1,0

Ainsi, lorsque vous l'avez envoyé au serveur, il essaiera de se lier à un flotteur de ce nom et il ne le trouvera pas car il est venu en tant que 1 au lieu de 1.0. Il est très boiteux et fou que les ingénieurs de MS n'aient pas trouvé de solution par défaut à cela. Je trouve que si vous enchaînez tout, les liaisons sont assez intelligentes pour trouver des choses.

Donc, avant d'envoyer les données, exécutez-les via un stringifier qui convertira également toutes les valeurs en chaînes.

0
Chris Stephens

Toutes les réponses précédentes étaient excellentes pour m'indiquer une solution au problème similaire. Je devais POST x-www-form-urlencoding au lieu de application/json (option par défaut si le paramètre contentType est manquant) pour pouvoir passer __RequestVerificationToken et simultanément confronté à un problème lorsque les propriétés des objets se trouvant dans le tableau ne lient pas leurs valeurs. La façon de résoudre le problème est de comprendre le travail interne du classeur de modèle MVC.

Donc, fondamentalement, lorsque vous devez fournir un jeton de vérification, vous êtes limité avec un attribut de validation. Et vous devez fournir le jeton en tant que paramètre et non en tant que partie de l'objet JSON que vous envoyez. Si vous n'utilisez pas ValidateAntiForgeryToken, vous pouvez vous entendre avec JSON.stringify. Mais si vous le pouviez, vous ne pourriez pas passer le jeton.

J'ai reniflé du trafic vers le backend lorsque ContentType était x-www-form-urlencoding et j'ai remarqué que mon tableau d'objets complexes était sérialisé en quelque chose comme ça: klo[0][Count]=233&klo[0][Blobs]=94. Ce tableau faisait initialement partie d'un objet racine, disons un modèle. Cela ressemblait à ça: model.klo = [{ Count: 233, Blobs: 94}, ...].

Côté backend, cette propriété klo était créée par un classeur MVC avec le même nombre d'éléments que j'ai envoyé. Mais ces éléments eux-mêmes n'ont pas obtenu de valeurs pour leurs propriétés.

[~ # ~] solution [~ # ~]

Pour résoudre ce problème, j'ai exclu la propriété klo de l'objet modèle côté client. Dans la fonction ajax, j'ai écrit ce code:

data: $.param(model) + "&" + arrayOfObjectsToFormEncoding("klo", [{ Count: 233, Blobs: 94}, ...])
....

    function arrayOfObjectsToFormEncoding (modelPropertyName, arrayOfObjects) {
        var result = "";
        if (arrayOfObjects && typeof arrayOfObjects == "object") {
            for (var i = 0; i < arrayOfObjects.length; i++) {
                var obj = arrayOfObjects[i];
                if (obj) {
                    for (var p in obj) {
                        if (obj.hasOwnProperty(p)) {
                            result += encodeURIComponent(modelPropertyName + "[" + i + "]." + p) + "=" + encodeURIComponent(obj[p]) + "&";
                        }
                    }
                }
            }
        }

        if (result[result.length - 1] == "&") {
            result = result.substr(0, result.length - 1);
        }

        return result;
    }

La fonction transforme un tableau d'objets complexes en une forme reconnue par MVC-binder. Le formulaire est klo[0].Count=233&klo[0].Blobs=94.

0
kokosda