web-dev-qa-db-fra.com

ASP.NET 5/MVC 6 Ajax après modèle sur contrôleur

Dans mon application ASP.NET 5 MVC 6, je souhaite envoyer des données avec Ajax à mon contrôleur. Je l'ai déjà fait avec ASP.NET MVC 5 et j'ai testé exactement le même code dans un projet ASP.NET MVC 5 vierge et cela a fonctionné, mais avec la nouvelle version, je ne peux pas et ne sais pas pourquoi. Avec l'appel Ajax, je peux accéder au contrôleur, le modèle est créé mais les champs sont nuls (ou faux pour le booléen). Voici mon code:

script.js:

var data = {
            model: {
                UserName: 'Test',
                Password: 'Test',
                RememberMe: true
            }
        };

        $.ajax({
            type: "POST",
            url: "/Account/Login/",
            data: JSON.stringify(data),
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (msg) {
                // Do something interesting here.
            }
        });

AccountController.cs:

[HttpPost]
    public JsonResult Login(LoginViewModel model)
    {
        if (ModelState.IsValid)
        {
            //var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
            //if (result.Succeeded)
            //{
            //     //return RedirectToLocal(returnUrl);
            //}

            ModelState.AddModelError("", "Identifiant ou mot de passe invalide");
            return Json("error-model-wrong");
        }

        // If we got this far, something failed, redisplay form
        return Json("error-mode-not-valid");
    }

LoginViewModel.cs:

public class LoginViewModel
{
    [Required]
    [Display(Name = "UserName")]
    [EmailAddress]
    public string UserName { get; set; }

    [Required]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [Display(Name = "Remember me?")]
    public bool RememberMe { get; set; }
}

Des idées? Merci

26
Christophe Gigax

Vous devez utiliser explicitement FromBody sur MVC6 si vous utilisez json

public JsonResult Login([FromBody]LoginViewModel model)

MODIFIER

Je pense que vous mélangez différentes erreurs. Je vais essayer de décrire comment vous devriez faire la demande:

content-type doit être : application/json

le corps de votre demande doit être au format JSON (comme suggéré par JasonLind):

{
    UserName: 'Test',
    Password: 'Test',
    RememberMe: true
};

c'est ce que vous devriez voir lors de l'inspection de la demande (via les outils de débogage de chrome F12) ou à l'aide d'un inspecteur de demandes comme fiddler.

Si vous voyez quelque chose sous la forme de UserName=Test&Password=Test&RememberMe=true alors vous le faites mal, c'est le format de formulaire.

vous n'avez pas besoin de la variable model. si vous voyez votre requête avec un "wrapper", vous devez la supprimer.

30
Bart Calixto

Vous pouvez implémenter BindModel vous-même! récupérez la chaîne json et désérialisez-la sur votre entité.

public class JsonBinder<T> : System.Web.Mvc.IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        using (var reader = new System.IO.StreamReader(controllerContext.HttpContext.Request.InputStream))
        {
            //set stream position 0, maybe previous action already read the stream.
            controllerContext.HttpContext.Request.InputStream.Position = 0;
            string json = reader.ReadToEnd();
            if (string.IsNullOrEmpty(json) == false)
            {
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                object jsonData = serializer.DeserializeObject(json);
                return serializer.Deserialize<T>(json);
            }
            else
            {
                return null;
            }
        }
    }
}

et définir le JsonBinder à la méthode post comme

[HttpPost]
public JsonResult Login([ModelBinder(typeof(JsonBinder<LoginViewModel>))] LoginViewModel model)
{
    if (ModelState.IsValid)
    {
        //var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
        //if (result.Succeeded)
        //{
        //     //return RedirectToLocal(returnUrl);
        //}

        ModelState.AddModelError("", "Identifiant ou mot de passe invalide");
        return Json("error-model-wrong");
    }

    // If we got this far, something failed, redisplay form
    return Json("error-mode-not-valid");
}

l'autre solution

J'ai trouvé que vous pouviez définir DataContract sur la classe de Model et définir DataMember sur Propriétés de la classe.

éditer la classe comme ça

[DataContract]
public class LoginViewModel
{
    [DataMember]
    public string UserName { get; set; }
    [DataMember]
    public string Password { get; set; }
    [DataMember]
    public bool RememberMe { get; set; }
}

et vous devriez ajouter la référence de bibliothèque "System.Runtime.Serialization"

J'espère que cela peut fonctionner pour vous.

1
oudi

Ok, j’ai trouvé la solution, mais c’est quand même étrange pour moi ……………. Il vous suffit de supprimer le content-type de la requête.

$.ajax({
            type: "POST",
            url: "Account/Login",
            data: data,
            success: function (msg) {
                // Do something interesting here.
            }
        });

J'ai trouvé la solution grâce à l'observateur :) J'ai regardé la variable Request et j'ai trouvé que Request.Form renvoyait toujours une exception. Il disait que j'avais utilisé un type de contenu incorrect, alors j'ai simplement supprimé le content-type de mon message ajax et cela a fonctionné comme un charme. Je pense que, pour chaque message ajax que vous ferez, vous devez faire attention à ce que votre Request.Form soit rempli correctement

enter image description here

EDIT: Cette solution ne fonctionne que si vous pouvez publier des données autres que JSON (données très simples), telles que les données de connexion. Mais si je veux poster des données complexes, comme une liste, cela ne fonctionne plus ...

0
Christophe Gigax

Votre problème n'est pas MVC6 est jQuery. $ .ajax () vérifie les "données" et connaît son format afin de définir le type de contenu pour votre, et vous devriez aussi utiliser $ .ajax de façon prometteuse 

vérifier promesses vantages ici

et aussi sélectionner le formulaire et juste se transformer en objet, comme

 $('.anyForm').on('submit', fucntion(){
        //you have the form in JSON format
        var data = $(this).serializeObject()

    })

et here est la méthode serializeObject (), elle n’est pas sur jQuery par défaut.

0

Ça ne devrait pas être:

 var data = {
            UserName: 'Test',
            Password: 'Test',
            RememberMe: true

    };
0
JasonLind