Reputation: 3470
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
Upvotes: 29
Views: 48857
Reputation: 677
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.
Upvotes: 1
Reputation: 1
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.
Upvotes: 0
Reputation: 19705
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.
Upvotes: 35
Reputation: 3470
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
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 ...
Upvotes: 0
Reputation: 370
Shouldn't it be:
var data = {
UserName: 'Test',
Password: 'Test',
RememberMe: true
};
Upvotes: 0