ModelState is cleared, but data still shows - c#

When a user logs in, the normal thing to do is NOT give the password back. I am attempting this in MVC 5, and cannot make it happen. I have tried model.PASSWORD = "", as well as ModelState.Clear(), but the data is still showing.
Controller:
public async Task<ActionResult> Login(Login model) {
if(ModelState.IsValid) {
User temp = await db.Users.Where(u => u.USERNAME == model.USERNAME).FirstOrDefaultAsync();
if(temp != null) {
try {
if(Password.VerifyPass(model.PASSWORD, temp.PASSWORD)) {
LoginUser(temp);
return RedirectToAction("Index", "EMR");
}
} catch { }
}
}
ModelState.Clear();
model.PASSWORD = "";
ModelState.AddModelError("", "Username/password is unknown, or model was invalid!");
return View();
}
Model:
public class Login {
[Display(Name = "Username")]
public string USERNAME { get; set; }
[Display(Name = "Password")]
public string PASSWORD { get; set; }
}
}
View:
#using(Html.BeginForm()) {
#Html.AntiForgeryToken()
<div class="form-horizontal">
<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", #type = "password" } })
#Html.ValidationMessageFor(model => model.PASSWORD, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Login" class="btn btn-success" />
</div>
</div>
</div>
}
Upon looking at both the ModelState variable and the model variable in VisualStudio debug mode, they BOTH show the data as "". What could possibly be causing this?

As I had commented, one possible solution is to simply use #Html.PasswordFor(). This will render an input that is semantically correct for password input, which implicitly will hint for the browser to not autofill. So, instead of #Html.EditorFor, try this:
#Html.PasswordFor(model => model.PASSWORD, new { htmlAttributes = new { #class = "form-control" })

You are not passed the model back to view after clear it.
return View(model);
Still not working try as below.
var newModel = new Login { Password = "" };
ModelState.Clear();
return View(newModel);

i think its due to the browser cashing, annotate login action with
[OutputCacheAttribute(VaryByParam = "*", Duration = 0, NoStore = true)]
in addition to
return View(model);

First decorate your password field with Password Datatype.
public class Login
{
[Display(Name = "Username")]
public string USERNAME { get; set; }
[Display(Name = "Password")]
[DataType(DataType.Password)]
public string PASSWORD { get; set; }
}
And in the view, use the PasswordFor helper method.
#Html.PasswordFor(x=>x.PASSWORD)
With this, you do not need to explicitly set the password field to empty string. MVC will do that for you.

#CalebHuggins it looks like it is the browser who is rememberign the input fields values and populates them. You can try setting autocomplete attribute of your textboxes to "off" to avoid this.
Your model bound textbox may look like as following.
#Html.TextBoxFor(x => x.Something, new { autocomplete="off" } )
Thnaks and regards,
Chetan Ranpariya

Related

Validation Message doesn't show for model as property in viewmodel

i've been struggling with Model validation message in asp.net
I've got a model used by a viewmodel.
I want my view to display validation errors if users don't fill required fields.
My ModelState.IsValid is false when required fields are not fill (expected behaviour) but i can't see
any error message
My Model Class :
public class Model
{
[Required(ErrorMessage = "Name is required.")]
public string Name { get; set; }
[Required(ErrorMessage = "Adress is required.")]
public string Adress { get; set; }
}
My ViewModel Class:
public class ViewModel
{
[Required]
public Model SelectedModel { get; set; }
public string Title { get; set;}
}
My Controller:
[HttpPost]
public ActionResult Create(ViewModel vm)
{
try
{
if (ModelState.IsValid)
{
bool result = *DatabaseStuff*
if(result == true)
{
return RedirectToAction("Index");
}
else
{
return View();
}
}
return RedirectToAction("Index",vm);
}
catch
{
return View();
}
}
My View
#model ViewModel
#using (Html.BeginForm("Create", "MyController", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="box box-primary">
<div class="box-header with-border">
<h4 class="box-title">ViewModel Form</h4>
</div>
<div class="box-body">
<div class="row">
<div class="col-md-12">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Title, htmlAttributes: new { #class = "control-label" })
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
<div class="form-group">
#Html.LabelFor(model => model.SelectedModel.Name, htmlAttributes: new { #class = "control-label" })
#Html.EditorFor(model => model.SelectedModel.Name, null, "SelectedModel_Name",new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SelectedModel.Name, "", new { #class = "text-danger", #data_valmsg_for = "SelectedModel_Name" })
</div>
<div class="form-group">
#Html.LabelFor(model => model.SelectedModel.Adress, htmlAttributes: new { #class = "control-label" })
#Html.EditorFor(model => model.SelectedModel.Adress, null, "SelectedModel_Adress",new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SelectedModel.Adress, "", new { #class = "text-danger", #data_valmsg_for = "SelectedModel_Adress" })
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="box-footer">
<input type="submit" value="Create" class="btn btn-success pull-right" />
</div>
</div>
</div>
}
Thanks.
I believe that you should return View when the ModelState is invalid.
Return View doesn't make a new requests, it just renders the view
without changing URLs in the browser's address bar.
Return RedirectToAction makes a new request and the URL in the browser's
address bar is updated with the generated URL by MVC.
try
{
if (ModelState.IsValid)
{
bool result = *DatabaseStuff*
if(result == true)
{
return RedirectToAction("Index");
}
else
{
return View();
}
}
return View();
}

Remote validation not working in ASP.NET MVC 5

Here I have create form which consists customer email field for which I am trying to check whether the entered email already exits or not and if exist show email already exists message.
To do this i have tried to use remote validation but the problem is that its not showing any error even though email exists, it not even hitting the controller in IsEmailExists method which is used for remote validation
Any help with my code will be a great help. Thank you
Below is my action in controller
public JsonResult IsEmailExists(string CustomerEmail)
{
emedicineEntities _db = new emedicineEntities();
return Json(!_db.Customers.Any(x => x.CustomerEmail == CustomerEmail), JsonRequestBehavior.AllowGet);
}
Below is my metadata
namespace eMedicine.Model
{
public class CustomerMetaDta
{
[Remote("IsEmailExists", "Customers", ErrorMessage = "EmailId already exists.")]
[Required(ErrorMessage = "Please Enter Emailw")]
public string CustomerEmail { get; set; }
}
}
Below is my partial class
namespace eMedicine.Model
{
[MetadataType(typeof(CustomerMetaDta))]
public partial class Customer
{
}
}
Below is my view consisting customer email
<link href="~/Content/Site.css" rel="stylesheet" />
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
#using (Html.BeginForm("Create", "Customers", FormMethod.Post, new { #id = "register" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.CustomerName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CustomerName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CustomerName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CustomerEmail, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CustomerEmail, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CustomerEmail, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PasswordHash, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PasswordHash, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PasswordHash, "", new { #class = "text-danger" })
</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>
}
Change your method signature as follows to include the Bind Prefix attribute/property.
public JsonResult IsEmailExists([Bind(Prefix="Customer.CustomerEmail")] string CustomerEmail)
{
emedicineEntities _db = new emedicineEntities();
return Json(!_db.Customers.Any(x => x.CustomerEmail == CustomerEmail), JsonRequestBehavior.AllowGet);
}
Now it should work!
Not sure what real problem with your source code, but i tried to reproduce in my side, it worked well.
Here are my source code.
namespace WebApplication1.Controllers
{
public class CustomerMetaDta
{
[Remote("IsEmailExists", "Customer", ErrorMessage = "EmailId already exists.")]
[Required(ErrorMessage = "Please Enter Emailw")]
public string CustomerEmail { get; set; }
}
[MetadataType(typeof(CustomerMetaDta))]
public partial class Customer
{
}
public partial class Customer
{
public string CustomerEmail { get; set; }
public string CustomerName { get; set; }
public string PasswordHash { get; set; }
}
public class CustomerController : Controller
{
public JsonResult IsEmailExists(string CustomerEmail)
{
//emedicineEntities _db = new emedicineEntities();
List<Customer> _db = new List<Customer>
{
new Customer { CustomerEmail = "hien#gmail.com"},
new Customer { CustomerEmail = "hien1#gmail.com"}
};
return Json(!_db.Any(x => x.CustomerEmail == CustomerEmail), JsonRequestBehavior.AllowGet);
}
// GET: Customer
public ActionResult Index()
{
return View();
}
}
}
Index.cshtml file:
#model WebApplication1.Controllers.Customer
#{
ViewBag.Title = "Index";
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>
#using (Html.BeginForm("Create", "Customer", FormMethod.Post, new { #id = "register" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.CustomerName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CustomerName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CustomerName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CustomerEmail, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CustomerEmail, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CustomerEmail, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PasswordHash, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PasswordHash, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PasswordHash, "", new { #class = "text-danger" })
</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>
}
It jumps to method IsEmailExists() and this is result output
May be you missed setting
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
in web.config?
If it's not hitting the controller that suggests it isn't a problem with the actual validation logic, but more an issue with how you're addressing the server.
There are a few things to check:
Is your remote validation code available to the client at all?
The first thing to consider is could this be a security/authentication issue. There are a few simple things you can do to check that:
If you have authentication attributes set on your controllers or methods, try commenting them out
try commenting out any other authentication code
If that doesn't fix it, then when you've got the app running in debug, try using Postman to call your remote validation endpoint and see whether:
Postman gets a 200 back from your method.
If so, put a breakpoint in your code and check it is actually getting executed.
If Postman can get to your endpoint then...
Is there an issue in your code?
I can't see anything obviously wrong in your code, but it is different to how I write validation code. This is an example of some working remote validation straight out of my code
This is the model property with the remote validation set:
[System.Web.Mvc.Remote(
action: "CheckExistingDocumentCode",
controller: "Documents",
AdditionalFields = "DocumentId",
HttpMethod = "POST",
ErrorMessage = "Code already exists")]
public string DocumentCode { get; set; }
This is the corresponding method in the Documents controller:
[HttpPost]
public async Task<ActionResult> CheckExistingDocumentCode(string DocumentCode, int DocumentId)
{
try
{
if (!await _documentValidationRules.IsExistingDocumentCodeAsync(DocumentCode, DocumentId))
{
return Json(true, JsonRequestBehavior.AllowGet);
}
return Json("This Document Code is already in use", JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json(ex.ToString(), JsonRequestBehavior.AllowGet);
}
}
You can see that I've explicitly named all the parameters on the model property just to be clear exactly what's going on.
Comparing my code to yours the main differences are:
Mines async. That shouldn't make any difference.
My contrller method is a POST (so is has the HttpPost attribute, which also means I needed to tell the model the HttpMethod was POST too)
My remote validation method takes two parameters, so I'm passing in an extra property via the AdditionalFields argument
I can't see what the issue is in your code, but try changing it a piece at a time to work more like mine (particularly, try making it a post method and naming the parameters) and see if that exposes any issues.
Hopefully something in the above will get you closer.
One thing no one mentioned which will cause your symptons: If the method needs to be executed by Anonymous users and you don't have the AllowAnonymous attribute on your method, the method will not fire (and the submit button won't do anything.
You can try to add this code to the bottom of your View:
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
This is the issue I had.
//[AcceptVerbs("Get,Post")]
//[AllowAnonymous]
public async Task<IActionResult> IsEmailInUse(string email)
{
var registration = await
_context.Registration.FirstOrDefaultAsync(m => m.Email == email);
if(registration == null)
{
return Json(true);
}
else
{
return Json($"Email {email} is already in use");
}
}
//In Model Class
public class Registration
{
[System.ComponentModel.DataAnnotations.Key]
public int EmpId { get; set; }
public string UserName { get; set; }
[Required]
[EmailAddress]
[Remote(action: "IsEmailInUse",controller: "Registrations")]
public string Email { get; set; }
}

ASP.Net MVC Unable to edit user because od the [Compare(Password)] in the class User

I have table named Korisnik (on my language, on english its User) and i added an Edit ActionResult in my Controller , but it wont work because of the [Compare("Lozinka")] that is comparing the password from the database and the added property PotvrdiLozinku, in other words i must enter the Confirm password in order to Submit the changes
namespace ProjekatFinalni.Models
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
public partial class Korisnik
{
public int KorisnikID { get; set; }
[DisplayName("Korisnicko ime:")]
[Required(ErrorMessage ="Molimo vas unesite korisnicko ime.")]
public string Korisnickoime { get; set; }
[DisplayName("Lozinka:")]
[DataType(DataType.Password)]
[Required(ErrorMessage = "Molimo vas unesite lozinku.")]
public string Lozinka { get; set; }
[DisplayName("Admin:")]
public bool DaLiJeAdmin { get; set; }
[DisplayName("Gost:")]
public bool Gost { get; set; }
[DisplayName("Pravo za unos:")]
public bool PravoUnosa { get; set; }
[DisplayName("Potvrdi lozinku:")]
[DataType(DataType.Password)]
[Compare("Lozinka",ErrorMessage ="Lozinke se ne poklapaju.")]
public string PotvrdiLozinku { get; set; }
public string LoginErrorPoruka { get; set; }
}
This is the Edit ActionResult in my controller
public ActionResult Edit(int id)
{
using (BazaProjekatEntities4 dbModel = new BazaProjekatEntities4())
{
return View(dbModel.Korisniks.Where(x => x.KorisnikID == id).FirstOrDefault());
}
}
[HttpPost]
public ActionResult Edit(int id,Korisnik k)
{
try
{
using (BazaProjekatEntities4 dbModel = new BazaProjekatEntities4())
{
dbModel.Entry(k).State = EntityState.Modified;
dbModel.SaveChanges();
}
return RedirectToAction("Izlistaj");
}
catch
{
return View();
}
}
And this is the Edit.cshtml
#model ProjekatFinalni.Models.Korisnik
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Korisnik</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.KorisnikID)
<div class="form-group">
#Html.LabelFor(model => model.Korisnickoime, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Korisnickoime, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Korisnickoime, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Lozinka, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Lozinka, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Lozinka, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PotvrdiLozinku, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PotvrdiLozinku, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PotvrdiLozinku, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DaLiJeAdmin, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
#Html.EditorFor(model => model.DaLiJeAdmin)
#Html.ValidationMessageFor(model => model.DaLiJeAdmin, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Gost, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
#Html.EditorFor(model => model.Gost)
#Html.ValidationMessageFor(model => model.Gost, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PravoUnosa, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
#Html.EditorFor(model => model.PravoUnosa)
#Html.ValidationMessageFor(model => model.PravoUnosa, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Sacuvaj" class="btn btn-default" />
</div>
</div>
</div>
}
Only want to edit the permissions(Admin, Gost and PravoUnosa
EDIT( Added the registration form that i used)
[HttpPost]
public ActionResult DodajiliIzmeni(Korisnik korisnikmodel)
{
using (BazaProjekatEntities4 Modelkorisnik = new BazaProjekatEntities4())
{
if(Modelkorisnik.Korisniks.Any(x=> x.Korisnickoime == korisnikmodel.Korisnickoime))
{
ViewBag.DuplicateMessage = "Korisnicko ime vec postoji.";
return View("DodajiliIzmeni", korisnikmodel);
}
Modelkorisnik.Korisniks.Add(korisnikmodel);
Modelkorisnik.SaveChanges();
}
ModelState.Clear();
ViewBag.SuccessMessage = "Registracija je uspela";
return RedirectToAction("Index", "Login");
}
You should create a view model specific for the view, which has the properties and validation attributes on them as needed by the view and use that to transfer data between your view and action method.
public class EditUserVm
{
public int Id { get; set; }
[DisplayName("Korisnicko ime:")]
public string UserName { get; set; }
[DisplayName("Admin:")]
public bool Admin { get; set; }
[DisplayName("Gost:")]
public bool Gost { get; set; }
[DisplayName("Pravo za unos:")]
public bool PravoUnosa { get; set; }
}
Now you will use this view model for your GET and POST action methods. In your GET action method, first create an object of this view model, then get your Korisniks object for the Id passed in, Read and map the property values to the view model object and pass it to the view.
public ActionResult Edit(int id)
{
using (var dbModel = new BazaProjekatEntities4())
{
var user = dbModel.Korisniks.FirstOrDefault(x => x.KorisnikID == id);
// to do: If user is NULL, return a "Not found" view to user ?
var vm = new EditUserVm { Id = id };
vm.UserName = user.UserName;
vm.Admin = user.Admin;
vm.Gost = user.Gost;
vm.PravoUnosa = user.PravoUnosa;
return View(vm);
}
}
Now makes sure your view is strongly typed to this view model because we are passing an object of the EditUserVm class to it.
#model YourNamespaceGoesHere.EditUserVm
#using (Html.BeginForm())
{
#Html.HiddenFor(a=>a.Id)
<label>#Model.UserName</label>
#Html.LabelFor(a=>a.Admin)
#Html.CheckBoxFor(a=>a.Admin)
#Html.LabelFor(a=>a.Gost)
#Html.CheckBoxFor(a=>a.Gost)
#Html.LabelFor(a=>a.PravoUnosa)
#Html.CheckBoxFor(a=>a.PravoUnosa)
<button type="submit" >Save</button>
}
Now you will use the same view model as the action method parameter. Inside the method, we will read again the User entity from the database and udpate only the field we want to
[HttpPost]
public ActionResult Edit(EditUserVm model)
{
var db = new BazaProjekatEntities4();
var user = db.Korisniks.FirstOrDefault(x => x.KorisnikID == model.Id);
// to do : Do a null check on user to be safe :)
// Map the property values from view model to entity object
user.Admin = model.Admin;
user.Gost = model.Gost;
user.PravoUnosa = model.PravoUnosa;
db.Entry(k).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
In short, create a view model with properties absolutely needed by the view/ your code and use that to transfer data between your action method and view.
The solution was very simple and it could be from the start, i just need to add the user.PotvrdiLozinku = user.Lozinka; that will tell it that the Confirm password is equal to Password (For the [Compare] that is in the User class. :)
[HttpPost]
public ActionResult Edit(EditUserVm model)
{
var db = new BazaProjekatEntities4();
var user = db.Korisniks.FirstOrDefault(x => x.KorisnikID == model.Id);
// to do : Do a null check on user to be safe :)
// Map the property values from view model to entity object
user.Admin = model.Admin;
user.PotvrdiLozinku = user.Lozinka; // this line was missing
user.Gost = model.Gost;
user.PravoUnosa = model.PravoUnosa;
db.Entry(k).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}

TryUpdateModel(model, prefix, includeProperties) returns false but some properties are still updated

I have a scenario where I am editing a model in an ASP.NET MVC5 application and when I submit blank values TryUpdateModel returns false (correctly) but when I return the view with the same model that was passed into TryUpdateModel one of the properties has been updated but others havent' - even though they are all invalid.
Repro:
Load /Machines/Edit?serialNumber=2
Clear Position and Name fields (manually using delete, backspace, or cut)
Click Update button to submit form
The Name property is
updated even though TryUpdateModel returns false.
Is this a string only thing because I don't have a problem with the MachinePosition property being updated no matter what order I provide the includedProperties for https://msdn.microsoft.com/en-us/library/system.web.mvc.controller.tryupdatemodel(v=vs.118).aspx#Anchor_12 in. Has anyone else come across this - it feels like I'm doing something wrong in my code because a side effect like this would surely be documented.
The only reason I noticed this was because I was returning to the main list of machines after attempting to do an invalid update and the noticed that the Name property was being updated for these machines yet when I debugged the code and stepped through it I wasn't hitting when the backing list was being updated/set... very confused.
My model:
public class Machine
{
[Required]
[Range(0, long.MaxValue)]
[Display(Name = "Serial")]
public long SerialNumber { get; set; }
[Required]
[Range(0, 1000)]
[Display(Name = "Position")]
public int MachinePosition { get; set; } = 0;
[Required]
[Display(Name = "Name")]
public string Name { get; set; }
}
My view:
#model MyWebApplication.Machine
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Update #Model.Name</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<!-- Have the serial number shown in a hidden field so that it will be passed through on submit -->
#Html.HiddenFor(model => model.SerialNumber)
<div class="form-group">
#Html.LabelFor(model => model.SerialNumber, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DisplayFor(model => model.SerialNumber, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.MachinePosition, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.MachinePosition, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.MachinePosition, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Update" class="btn btn-default" />
</div>
</div>
</div>
}
My controller:
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditMachine(long? serialNumber)
{
if (serialNumber == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
// Get by serial
Machine machineToUpdate = MachineRepository.Get(serialNumber.Value);
string failureMessage = $"Model validation failed.";
// Model state is valid at this point because we have the serial set
if (ModelState.IsValid)
{
string oldName = machineToUpdate.Name;
int oldPosition = machineToUpdate.MachinePosition;
if (TryUpdateModel(machineToUpdate, "", new string[] { "Name", "MachinePosition" }))
{
Result result = MachineRepository.UpdateMachine(machineToUpdate);
if (result.ResultCode == ResultTypeEnum.Success)
{
// Update the list used by the view to include the new entry
RestoreDatabaseBackup();
// Use PRG pattern to prevent resubmit on page refresh
return RedirectToAction("Edit", new { serialNumber = serialNumber.Value, machineName = $"{oldName}/{machineToUpdate.Name}" });
}
failureMessage = $"There was an error updating the machine: {result.ResultMessage}";
}
}
// Set failure message
ViewBag.UpdateResultMessage = failureMessage;
ViewBag.UpdateSucceeded = false;
// When I get to here the "Name" field has been overwritten with a blank value
return View(machineToUpdate);
}

ASP.NET MVC: ModelState error message not being displayed

I am just a beginner of ASP.NET MVC. I just started with creating my login page it works fine but I have a problem when the user inputs wrong credentials. This is what I have done:
UserProfile
public partial class UserProfile
{
public int UserId { get; set; }
[Display(Name = "User name")]
[Required(ErrorMessage = "Username is required.")]
public string UserName { get; set; }
[Display(Name = "Password")]
[DataType(DataType.Password)]
[Required(ErrorMessage = "Password is required.")]
public string Password { get; set; }
public bool IsActive { get; set; }
}
HomeController:
public class HomeController : Controller
{
public ActionResult Login()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(UserProfile objUser)
{
if (ModelState.IsValid)
{
using (DB_Entities db = new DB_Entities())
{
var obj = db.UserProfiles.Where(a => a.UserName.Equals(objUser.UserName) && a.Password.Equals(objUser.Password)).FirstOrDefault();
if (obj != null)
{
Session["UserID"] = obj.UserId.ToString();
Session["UserName"] = obj.UserName.ToString();
return RedirectToAction("UserDashBoard");
}
}
}
else
{
ModelState.AddModelError("", "Invalid Credentials");
}
return View(objUser);
}
public ActionResult UserDashBoard()
{
if (Session["UserID"] != null)
{
return View();
}
else
{
return RedirectToAction("Login");
}
}
}
And the View
#model MyWebApplication.Models.UserProfile
#{
ViewBag.Title = "Login";
}
#using (Html.BeginForm("Login", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<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">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Login" class="btn btn-default" />
</div>
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
I am not sure why the message "Invalid Credentials" is not being displayed when the user types a username that isn't stored in the database.
In your question you don't state whether or not in your if statement if the line ModelState.AddModelError("", "Invalid Credentials"); is being hit, which I doubt it is.
Your if statement is constructed wrong, because as of now you're only wanting to display the Invalid Credentials error if the ModelState is not valid.. not whether or not the user's credentials exist.
So you need to rewrite your if statement to this:
if (ModelState.IsValid)
{
using (DB_Entities db = new DB_Entities())
{
var obj = db.UserProfiles.Where(a => a.UserName.Equals(objUser.UserName) && a.Password.Equals(objUser.Password)).FirstOrDefault();
if (obj != null)
{
Session["UserID"] = obj.UserId.ToString();
Session["UserName"] = obj.UserName.ToString();
return RedirectToAction("UserDashBoard");
}
else
{
ModelState.AddModelError("", "Invalid Credentials");
}
}
}
Not to be picky, but hopefully your UserName property is unique, because if you have 2 users that have the same exact UserName and Password and you get the FirstOrDefault occurrence, then you could possibly be letting a user sign in with another's credentials. So I would ensure that the UserName property is unique and change FirstOrDefault to SingleOrDefault.
But if it does, then here is what needs to change:
Option 1
If you want the error message to be displayed under the textbox then in your controller change this:
ModelState.AddModelError("", "Invalid Credentials");
To:
ModelState.AddModelError("UserName", "Invalid Credentials");
AddModelError takes 2 parameters.. the key and the errorMessage for that key. The key is the property name in your model.
public void AddModelError(
string key,
string errorMessage
)
Option 2
If you do not want to display the error message under the textbox, but rather at the top of the form:
Then change this:
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
To:
#Html.ValidationSummary(false, "", new { #class = "text-danger" })
For option 2, you need to read the overloaded methods carefully. Per MSDN in correlation with how you have your ValidationSummary setup.. the first parameter is bool excludePropertyErrors.. you had that set to true so you were excluding Property errors, which is why I proposed to change that to false, so Property errors would be included.
public static MvcHtmlString ValidationSummary(
this HtmlHelper htmlHelper,
bool excludePropertyErrors,
string message,
IDictionary<string, object> htmlAttributes
)
Let me know if this helps.

Categories