I have the following MVC View and Controller. When I submit the form. It pass in the Id instead of the ChangeUserViewModel.
View:
#model AppTransactionsManagement.Models.ChangeUserViewModel
<div>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">Change Password</h4>
</div>
#using (Html.BeginForm("ChangePassword", "UsersAdmin", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="modal-body">
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.Email, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Password, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Password, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Password, "", new { #class = "text-danger" })
</div>
</div>
</div>
</div>
<div class="modal-footer">
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</div>
</div>
}
</div>
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
private async Task<ActionResult> ChangePassword(ChangeUserViewModel model)
{
if (ModelState.IsValid)
{
//Get the user manager and user. (if the user is not found, user will be null)
ApplicationUser user = await UserManager.FindByIdAsync(model.Id);
if (user == null)
{
return PartialView("_ChangePassword");
}
else
{
user.PasswordHash = UserManager.PasswordHasher.HashPassword(model.Password);
UserManager.UpdateSecurityStamp(user.Id.ToString());
//Save Changes
IdentityResult result = await UserManager.UpdateAsync(user);
if (result.Succeeded)
{
return Json(new { success = true });
}
else
{
var error = result.Errors.FirstOrDefault();
return PartialView("_ChangePassword");
}
}
}
return PartialView("_ChangePassword");
}
Model:
public class ChangeUserViewModel
{
public string Id { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "Email")]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
}
What am I doing wrong?
You have marked the method as private, which will make it inaccessible. You can't POST to a private method. This is most likely your problem. Change it to
[HttpPost]
public async Task<ActionResult> ChangePassword(ChangeUserViewModel model)
{
......
......
}
Try to use model binding in your controller action.
public async Task<ActionResult> ChangePassword([Bind(Include = "Id, Email, Password")]ChangeUserViewModel model) {}
Related
I have a Database where I save Users and Roles, with a many-to-many Relationship. I have those imported via EF Database first as Models. When I try to add, update or remove Roles from a Person, nothing happens, or for example if I press on add, the last Role doesnt show up anymore.
After debugging it a bit. I found out, that after the View gives back the Model (Person) the Roles list is suddenly empty. Why is this and how can I fix it?
This is my Controller Methode to Edit a Person
public async Task<ActionResult> Edit(int? id)
{
if (id == null)
{
return RedirectToAction("BadRequest", "Error", new { area = "" });
}
Person person = await db.Person.FindAsync(id);
if (person == null)
{
return RedirectToAction("NotFound", "Error", new { area = "" });
}
ViewBag.allRoles = db.Roles.ToList();
return View(person);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "id,nachname,vorname,winLogon")] Person person, string command, int? roleId)
{
Role none = new Role { id = -1, name = "None" };
var allRoles = db.Roles.ToList();
if (ModelState.IsValid)
{
if (command != null && command == "remove")
{
person.Roles.Remove(db.Roles.FirstOrDefault(x => x.id == roleId));
return View(person);
}
if (command != null && command == "add")
{
person.Roles.Add(none);
return View(person);
}
if (command != null && command == "save")
{
person.Roles.Remove(none);
db.Entry(userRoles.User).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
}
return View(person);
}
This is the Person Model
public partial class Person
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Person()
{
this.Roles = new HashSet<Role>();
}
public int id {get;set;}
public string nachname {get;set;}
public string vorname {get;set;}
public string winLogon {get;set;}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Role> Roles {get;set;}
}
And this is the Role Model
public partial class Rolle
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Rolle()
{
this.Person = new HashSet<Person>();
}
public int id { get; set; }
public string name { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Person> Person { get; set; }
}
And this is the View
#model Models.Person
#{
ViewBag.Title = "Edit";
List<Role> allRoles = ViewBag.allRoles;
}
<h2>Edit</h2>
#using (Html.BeginForm("Edit", "People", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Person</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.id)
<div class="form-group">
#Html.LabelFor(model => model.nachname, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.nachname, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.nachname, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.User.vorname, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.vorname, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.vorname, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.winLogon, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.winLogon, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.winLogon, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group col-md">
#for (int i = 0; i < Model.Roles; i++)
{
#Html.LabelFor(model => model.Roles, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Roles, new SelectList(allRoles, "id", "name", role), new { #class = "form-control-static col-md-1" })
<input type="hidden" name="roleId" value="#role.id" />
<input name="command" type="submit" value="remove" class="btn btn-default col-md-offset-1" />
</div>
}
<input name="command" type="submit" value="add" class="btn btn-default col-md-offset-1" />
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input name="command" type="submit" value="save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 1 year ago.
i created a login page and a view model for my login page
when i run created page this line will thrown an exception :
#Html.EditorFor(model => model.UserName, new { htmlAttributes = new { #class = "form-control" } })
this is complete login pages code:
#model DataLayer.LoginViewModel
<div class="form-horizontal">
<h4>LoginViewModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.UserName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.UserName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.UserName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Password, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Password, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Password, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Rememberme, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
#Html.EditorFor(model => model.Rememberme)
#Html.ValidationMessageFor(model => model.Rememberme, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
and this is my viewmodel :
public class LoginViewModel
{
[Required]
[MaxLength(150)]
public string UserName { get; set; }
[Required]
[MaxLength(150)]
public string Password { get; set; }
public bool Rememberme { get; set; }
}
this is controller :
ILoginRepository loginRepository;
MyCmsContext db = new MyCmsContext();
public AccountController()
{
loginRepository = new LoginRepository(db);
}
// GET: Account
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LoginViewModel login)
{
if (ModelState.IsValid)
{
if (loginRepository.IsExistUser(login.UserName,login.Password))
{
FormsAuthentication.SetAuthCookie(login.UserName, login.Rememberme);
return Redirect("/");
}
else
{
ModelState.AddModelError("UserName", "User not found");
}
}
return View();
}
}
Keep the code simple
Make sure you are instantiating the viewmodel inside the Action Method and passing it to the view as shown below
[HttpGet]
public ActionResult Login()
{
LoginViewModel lgVM= new LoginViewModel();
return View(lgVM);
}
[HttpPost]
public ActionResult Login(LoginViewModel login)
{
return View();
}
Keep your markup simple as
#Html.EditorFor(model => model.UserName, new{ #class = "form-control" })
I am new to ASP.NET MVC 5 and having some issues with binding only the multi-select section of the body during POST when submitting the form. The form renders correctly, with checkboxes being correctly selected. The form is scaffolded using visual studio (except the multi-select)
public class EditViewModel
{
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "LastName")]
public string LastName { get; set; }
public IList<RolesViewModel> Roles { get; set; }
}
public class RolesViewModel
{
public string RoleId { get; set; }
public string RoleName { get; set; }
public bool Selected { get; set; }
}
[HttpGet]
public async Task<ActionResult> Edit(string Id)
{...
var model = Mapper.Map<ApplicationUser, EditViewModel>(user);
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(EditViewModel model)
{
}
#model BloggingService.Web.Models.EditViewModel
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Edit</title>
</head>
<body>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Edit</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FirstName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LastName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.LastName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LastName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Roles, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#for (int i = 0; i < Model.Roles.Count; i++)
{
#Html.CheckBoxFor(x => #Model.Roles[i].Selected,"test1");
<div class="form-check">
<input type="hidden" asp-for="#Model.Roles[i].RoleId" />
<input type="hidden" asp-for="#Model.Roles[i].RoleName" />
<input type="checkbox" asp-for="#Model.Roles[i].Selected" class="form-check-input" checked="#Model.Roles[i].Selected" />
<label class="form-check-label" asp-for="#Model.Roles[i].Selected">
#Model.Roles[i].RoleName
</label>
</div>
}
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Would really appreciate any insight.
I have managed to find the correct and cleaner approach for accomplishing such tasks by utilizing the HTML helper methods inside my for loop and now the data are able to bind correctly during data-binding.
#Html.HiddenFor(x => #Model.Roles[i].RoleId);
#Html.HiddenFor(x => #Model.Roles[i].RoleName);
#Html.CheckBoxFor(x => #Model.Roles[i].Selected);
I'll go over my investigation and hopefully you guys can figure out an idea for what I should do next.
The relevant M, V and C for the login look like
public class LoginViewModel
{
[Required]
[Display(Name = "Email")]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
and
#using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Password, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Password, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" style="margin-bottom: 40px;">
<div class="col-md-10">
<div class="checkbox">
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe)
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<input type="submit" value="Log in" class="btn btn-default" style="padding-left: 40px; padding-right: 40px;" />
</div>
</div>
}
and
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
What I'm trying to do is simply have the login be by username rather than email address. While I can't find anything in the MVC template source code pertaining to username, I looked in the AspNetUsers table from the template and see that there is indeed a column called UserName. The trick is try and change the above source code to do what I want without causing any side effects elsewhere in the MVC template.
I changed the M to
public class LoginViewModel
{
[Required]
[Display(Name = "UserName")]
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; }
}
and the V to
#using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.UserName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.UserName, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.UserName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Password, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Password, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" style="margin-bottom: 40px;">
<div class="col-md-10">
<div class="checkbox">
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe)
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<input type="submit" value="Log in" class="btn btn-default" style="padding-left: 40px; padding-right: 40px;" />
</div>
</div>
}
The changing of the C stuff is what I'm stuck on. I know that I could do something like changing
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
to
var email = pingDatabaseAndGetEmailFromUsername(model.UserName);
var result = await SignInManager.PasswordSignInAsync(email, model.Password, model.RememberMe, shouldLockout: false);
But, there has to be a better way, right?
Also, it's possible that I screwed up other stuff by modifying LoginViewModel because other bits of code in the MVC template might use it and expect it to have an Email field.
So what should I actually be doing here? I want simplest change.
If I do have to change PasswordSignInAsync, where is the implementation? I can't find it.
The default template implementation uses the email address as the username, which is why it's sending the model.Email value to the PasswordSignInAsync function. Assuming you created the user accounts with separate Email and UserName values, it should be as simple as changing the sign in method to:
var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
Again, this assumes your user accounts were created with distinct UserName and Email values.
I have an actionmethod resetpassword which is of type get which returns a view. The method gets called from an actionlink button. To this view I am passing a user obj. Now when I click on the actionlink , it goes to the view but as I have applied validationfor the validations are getting fired automatically when the view is loaded. Is this because I am passing an obj of user to the view.? If that's the case, then how can i turn off the validations for HttpGet for that action method as I only want to load the inputs and when the user starts filling in the inputs then only validation should fire.
Action Method.
[ValidateInput(false)]
[HttpGet]
[ActionName("ResetPassword")]
public ActionResult ResetPassword(UserBE user)
{
user.Email = TempData["userEmail"].ToString();
return View(user);
}
View
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
#model XYZ.BE.UserBE
#{
ViewBag.Title = "ResetPassword";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>ResetPassword</h2>
#using (Html.BeginForm("ResetPassword", "User"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
</div>
<div class="form-group">
#Html.LabelFor(model => model.Email, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DisplayFor(model=>model.Email)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Password, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.PasswordFor(model => model.Password)
#Html.ValidationMessageFor(model => model.Password)
#Html.HiddenFor(model=>model.Email)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.NewPassword, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.PasswordFor(model => model.NewPassword)
#Html.ValidationMessageFor(model => model.NewPassword)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ConfirmedPassword, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.PasswordFor(model => model.ConfirmedPassword)
#Html.ValidationMessageFor(model => model.ConfirmedPassword)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Reset Password" class="btn btn-default" />
</div>
</div>
}
ActionLink BUtton
<h3>#Html.ActionLink("Reset Password", "ResetPassword")
Post Method
[HttpPost]
[ActionName("ResetPassword")]
public ActionResult ResetPasswordPost(UserBE user)
{
user = UserBL.AuthenticateUser(user);
if (!user.AuthenticUser || (user.Password==user.NewPassword))
{
return View(user);
}
else
{
return UserBL.ResetPassword(user)?View("LoginSuccessful",user):View(user);
}
}
Model
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; }
private bool authenticUser = false;
public bool AuthenticUser
{
get { return authenticUser; }
set { authenticUser = value; }
}
[Required(ErrorMessage = "Password is required")]
public string NewPassword { get; set; }
[Required(ErrorMessage = "Confirm passord and NewPassWord does not match")]
[Compare("NewPassword")]
public string ConfirmedPassword { get; set; }
I just added the following to _layout and it worked.
#Scripts.Render("~/bundles/jqueryval")