ASP.NET MVC Model property is null when POST method is called
Solution 1
currentUserId
isn't submitted to the post action method because of two reasons:
currentUserId
isn't a public property.- It's not present inside
using (Html.BeginForm
block in<input>
,<textarea>
or<select>
tags.
Remove currentUserId
and create a new public property named CurrentUserId
in your model. You also don't need the GetUserId()
method. Your model should look like below
public class ChangePasswordBindingModel
{
[Required]
[DataType(DataType.Password)]
//[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
//[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[Required]
[DataType(DataType.Password)]
//[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string CurrentUserId { get; set; }
public ChangePasswordBindingModel()
{
}
public ChangePasswordBindingModel(string userId)
{
this.CurrentUserId = userId;
}
}
and use a HiddenField to include CurrentUserId
in your view
@using (Html.BeginForm("ChangePassword","Account", FormMethod.Post))
{
@Html.AntiForgeryToken();
@Html.HiddenFor(x => x.CurrentUserId)
<input type="hidden" name="returnUrl" value="@ViewBag.returnUrl" />
<div class="form-group">
<label>Current Password</label>
@Html.PasswordFor(x => x.OldPassword, new { @class = "form-control" })
</div>
<div class="form-group">
<label>New Password</label>
@Html.PasswordFor(x => x.NewPassword, new { @class = "form-control" })
</div>
<div class="form-group">
<label>Re-enter New Password</label>
@Html.PasswordFor(x => x.ConfirmPassword, new { @class = "form-control" })
</div>
<!-- <button class="btn btn-primary" type="submit">Save</button> -->
<input class="btn btn-primary" type="submit" value="Save" />
}
In your post action method, you can get the value of CurrentUserId
property as below
result = await UserManager.ChangePasswordAsync(loginChange.CurrentUserId, loginChange.OldPassword, loginChange.NewPassword);
Solution 2
First make currentUserId public then put that in a hidden field in your View.
@Html.HiddenFor(x => x.currentUserId )
Solution 3
currentUserId
is a private member, not a property. And it is not bound to any form field in the view. So when you render the view by passing a model which has a non null value of currentUserId
, that value is immediately lost when the view is rendered on the client, because it only exists in the model on the server. So there is no way to get it back.
You can create a CurrentUserId
property, and have a hidden field in your view using Html.HiddenFor()
, so that the value gets sent to the client and you get it back during POST.
ITWorker
Updated on July 20, 2022Comments
-
ITWorker almost 2 years
In a controller action, I have the following call, where
userId
is a known non-null value:return View("ChangePassword", new ChangePasswordBindingModel(userId));
Here is the definition of ChangePasswordBindingModel:
public class ChangePasswordBindingModel { private string currentUserId; [Required] [DataType(DataType.Password)] //[Display(Name = "Current password")] public string OldPassword { get; set; } [Required] //[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "New password")] public string NewPassword { get; set; } [Required] [DataType(DataType.Password)] //[Display(Name = "Confirm new password")] [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] public string ConfirmPassword { get; set; } public ChangePasswordBindingModel() { } public ChangePasswordBindingModel(string userId) { this.currentUserId = userId; } public string GetUserId() { return this.currentUserId; } }
Here is the view that is bound to this Model for ChangePassword:
@model IdentityDevelopment.Models.ChangePasswordBindingModel @{ ViewBag.Title = "ChangePassword"; } @Html.ValidationSummary(false) <h2>Change Password</h2> @using (Html.BeginForm("ChangePassword","Account", FormMethod.Post)) { @Html.AntiForgeryToken(); <input type="hidden" name="returnUrl" value="@ViewBag.returnUrl" /> <div class="form-group"> <label>Current Password</label> @Html.PasswordFor(x => x.OldPassword, new { @class = "form-control" }) </div> <div class="form-group"> <label>New Password</label> @Html.PasswordFor(x => x.NewPassword, new { @class = "form-control" }) </div> <div class="form-group"> <label>Re-enter New Password</label> @Html.PasswordFor(x => x.ConfirmPassword, new { @class = "form-control" }) </div> <!-- <button class="btn btn-primary" type="submit">Save</button> --> <input class="btn btn-primary" type="submit" value="Save" /> }
And lastly, here is the Post method for clicking
Save
on the above form:[HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> ChangePassword(ChangePasswordBindingModel loginChange) { if (ModelState.IsValid) { IdentityResult result = null; try { string userid = loginChange.GetUserId(); result = await UserManager.ChangePasswordAsync(userid, loginChange.OldPassword, loginChange.NewPassword); } catch (Exception ex) { } if (result != null && result.Succeeded) { return RedirectToAction("Index"); } else { if(result != null) { AddErrorsFromResult(result); } } } return View(loginChange); }
The problem is that the
userid
does not get set to a non-null value, as I thoughtGetUserId()
would do. Why does thecurrentUserId
property of the Model that was sent to the post method not contain the value? I confirmed this is so by stepping into the call toGetUserId()
during debugging. -
ITWorker over 7 yearsThanks for the explanation.
-
ITWorker over 7 yearsThanks for the help.