29

In my ASP.NET 5 MVC 6 application, I want to post with Ajax some data to my controller. I already done this with ASP.NET MVC 5 and I tested the exact same code in an blank ASP.NET MVC 5 project and it worked, but with the new version I can't and I don't know why. With the Ajax call, I can go to the controller, the model is created but the fields are null (or false for the boolean). Here is my 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; }
}

Any ideas ? Thanks

4
  • tried with .done instead of success? Commented Feb 28, 2015 at 14:48
  • This is not the problem. I'm successfully go to the controller, but the fields inside my model are null. Commented Feb 28, 2015 at 16:08
  • your DTO seems to be correct. hmmm Commented Mar 1, 2015 at 3:49
  • maybe the model binder is not binding the data from AJAX properly with LoginViewModel object. I experienced a similar problem due to the difference in current culture ( current culture was non-english and double values from AJAX were not bounded properly ) Commented Apr 16, 2015 at 12:15

5 Answers 5

35

You need to explicit use FromBody on MVC6 if you are using json

public JsonResult Login([FromBody]LoginViewModel model)

EDIT

I think you are mixing different errors. I will try to describe how you should make the request:

content-type must be: application/json

your request body must be in JSON format (as JasonLind suggested):

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

this is what you should see when inspecting the request (via chrome debugger tools F12) or using a request inspector like fiddler.

If you see something in the form of UserName=Test&Password=Test&RememberMe=true then you are doing it wrong, that's form format.

you don't need the model variable. if you see your request with a "wrapper" then you should remove it.

Sign up to request clarification or add additional context in comments.

5 Comments

I already tried it but it doesn't work too. My viewmodel is created but it's empty.
Ok, I retested with this data : var data = { UserName: 'Test', Password: 'Test', RememberMe: true }; And it worked. Thanks ! :p
great, just stated what's needed to be in the request. glad you solved it :D
if it doesn't work for someone, please make sure that you are using a correct content type. it should be "application/json; charset=utf-8"
I'm trying to do the same thing. Has there been any breaking updates to this solution?
1

You can implement BindModel yourself! get the json string and deserialize to your entity.

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

and set the JsonBinder to the post method like

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

the other solution

I found that you could set DataContract to the class of Model, and set DataMember to the Properties of the class.

edit the class like this

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

and you should add library reference "System.Runtime.Serialization"

Hope it can works for u.

3 Comments

(This post does not seem to provide a quality answer to the question. Please either edit your answer and improve it (maybe with some sample source code), or just post it as a comment to the question.)
The [FromBody] attriubte works only with ASP.NET Core
Yes, [FromBody] only exist in .NET Core, I found IModelBinder can do the same. @christophe-gigax
0

Shouldn't it be:

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

    };

1 Comment

Why it shouldn't be object?
0

Ok I found the solution, but it's still strange for me ... You just have to remove the content-type from the request.

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

I've found the solution thanks to the watcher :) I watched the Request variable, and I found that Request.Form thrown always an exception. It said that I used a wrong content-type, so I just removed the content-type from my ajax post and it worked like a charm. I think that, for every ajax post you will make, you have to be careful that your Request.Form is filled correctly

enter image description here

EDIT: This solution works only when you could post non-json data (very simple data), like connexion data. But if I want post complex data, like a list, it doesn't work anymore ...

2 Comments

If you want to use content type then don't use json.stringify.
I tested without json.stringify and with my content-type and it still doesn't work, fields were always null.
0

Your problem is not MVC6 is jQuery. $.ajax() check the "data" and know its format so sets the content type for your, and also you should use $.ajax in a promise fashion

check promises vantages here

and also select the form and just turn into to object, like

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

    })

and here is serializeObject() method, it is not on jQuery by default.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.