ASP.NET MVC: ModelState error message not being displayed - c#

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.

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

ModelState is cleared, but data still shows

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

How can I use the Reset Password Functionality in Web API 2 [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I have created a method so that when user requests for password reset, it sends an email to user. I want now to generate a url which opens ResetPassword.cshtml from web site to be opened. I am not able to hit website, because the web site page is asking for cookies which I do not have.
First generate an url for reset password page:
string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
//For MVC controller
var callbackUrl = Url.Action("ResetPassword", "Account", new { code = code }, protocol: Request.Url.Scheme);
//For Web API controller
var callbackUrl = Url.Link("Default", new { Controller = "Account", Action = "ResetPassword", code = code });
After it create MVC controller with reset password method:
[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
return code == null ? View("Error") : View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
}
var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
//...
}
AddErrors(result);
return View();
}
Then create the model to accept new password:
public class ResetPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
Finally create view for resetting password:
#model TreeTag.Models.ResetPasswordViewModel
#{
ViewBag.Title = "Reset password";
}
<h2></h2>
#using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Reset your password.</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Code)
<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" })
</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" })
</div>
</div>
<div id="confirm_password" class="form-group">
#Html.LabelFor(m => m.ConfirmPassword, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.ConfirmPassword, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Reset" />
</div>
</div>
}

Validate MVC field at client side

I have problem validating one field at client side, Here is my code:
Model:
public class Registration
{
public int Id { get; set; }
[IsUserExistAttribute(ErrorMessage = "Email already exists!")]
[EmailAddress(ErrorMessage = "Please enter valid Email")]
[Required(ErrorMessage = "Email is required")]
public string Email
{
get; set;
}
[MustBeTrue(ErrorMessage = "Please Accept the Terms & Conditions")]
public bool TermsAndConditions { get; set; }
}
ValidationAttribute:
public class IsUserExistAttribute : ValidationAttribute,IClientValidatable
{
public override bool IsValid(object email)
{
DemoDbContext demoContext=new DemoDbContext();
string emailString = Convert.ToString(email);
if (demoContext.Registrations.Any(a=>a.Email.Contains(emailString)))
{
return false;
}
return true;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = this.ErrorMessage,
ValidationType = "emailvalidate"
};
}
}
View:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Registration</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<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.TermsAndConditions, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
#Html.EditorFor(model => model.TermsAndConditions)
#Html.ValidationMessageFor(model => model.TermsAndConditions, "", 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>
}
Everthing upto this works fine, But my question is This IsUserExistAttribute doesn't validate it on client side, on server side it validates and give the message like Email already exists!
But, I want it to validate client side too. So, what is the good way to achieve this? Other approach is also welcome :)
I tried something like this seems no success:
$.validator.addMethod("emailvalidate", function (value, element) {
var is_valid = false;
$.ajax({
// as before...
async: false,
success: function (data) {
is_valid = data === 'True';
}
});
return is_valid;
}, "Username not available.");
MVC already comes with a RemoteAttribute for client side validation (refer How to: Implement Remote Validation in ASP.NET MVC) so there is little point in you trying to reinvent the wheel. (As a side note, your currently missing the $.validator.unobtrusive.adapters.add() method necessary to add the rules for client side validation).
But there are a few issues with your current validation attribute. A validation attribute should not be accessing the database and you have made it impossible to unit test your code. And you will be repeating the code in the attribute again in the controller method that is called by the ajax function, violating the DRY principal and making it harder to maintain your code.
Instead, create a private method in your controller (or a public method in a separate service) for you database code, for example
private bool IsEmailUnique(string email)
{
return !db.Registrations.Any(a => a.Email.Contains(email)))
}
Then for client side validation, decorate you property with the RemoteAttribute
[Remote("IsEmailValid", "ControllerName", ErorMessage = "...")]
public string Email { get; set; }
and add the controller method
public JsonResult IsEmailValid(string email)
{
bool isValid = IsEmailUnique(email);
return Json(isValid, JsonRequestBehavior.AllowGet);
}
and then if you also want to have server side validation when you submit the form, call the same method and if not valid, add a ModelStateError
public ActionResult Register(Registration model)
{
bool isValid = IsEmailUnique(model.Email);
if (!isValid)
{
ModelState.AddModelError("Email", "...");
}
if (!ModelState.IsValid)
{
return View(model);
}
// save and redirect
}

how to hold temporary value object in a actionresult and use it in another actionresult

hi i want to hold a temporary code eg."codes" in an actionmethod and use it on another actionmethod to compare if the model.code entered in the view textbox is"codes".But i dont want to save it to database
this is my first actionresult method which will hold the value
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> PhoneReset(ForgotPasswordView model, string sms)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByEmailAsync(model.Email);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
if (user != null || user.PhoneNumber==model.cellNumber)
{
//string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
//var forgot = new ForgotPasswordBusiness();
GenerateCodeBusiness gen = new GenerateCodeBusiness();
var codes = gen.CreateRandomPassword(8);
SendSmsBusiness objap = new SendSmsBusiness();
sms = "Your password reset code is " + codes;
objap.Send_SMS(model.cellNumber, sms);
await SignInAsync(user, isPersistent: false);
return RedirectToAction("ResetViaPhone", "Account");
}
else if (user == null)
{
ModelState.AddModelError("", "The user does not exist");
return View();
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
this is the second actionresult
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null)
{
ModelState.AddModelError("", "No user found.");
return View();
}
IdentityResult result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
AddErrors(result);
return View();
}
this is the resetPassword view where the code is entered.
#model Template.Model.ResetPasswordViewModel
#{
ViewBag.Title = "Reset password";
}
<h2>#ViewBag.Title.</h2>
#using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Reset your password.</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Code)
<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" })
</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" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.ConfirmPassword, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.ConfirmPassword, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Reset" />
</div>
</div>
}
this is the resetpassword model
public class ResetPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
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; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
any assistance will be of great help
Use TempData -
//Save values in tempData
TempData["codes"] = codes;
//Fetching the values in different action method
//Note: Cast the tempdata value to its respective type, as they are stored as object
var codes = TempData["codes"];
Please note that value in TempData will be available only till you fetch its value. Once fetched the value, it will be cleared.
The values in Tempdata are stored as objects, so you will need to cast the TemtData value to its respective type.
https://msdn.microsoft.com/en-in/library/dd394711%28v=vs.100%29.aspx

Categories