I'm trying to figure out how to validate that a user has entered matching passwords when they sign up. Is there anything built in to MVC 4 Data Annotations that I can use for this or is the only route creating a custom validation attribute?
If I do have to create a custom validation attribute, how do I access the password property (assuming I put the annotation on the confirm password property)? Also, are there any commonly used libraries for this type of validation?
This is what I have for the beginning of a custom validation attribute, just not sure how to access the password property:
public class CrossFieldValidationAttribute : ValidationAttribute
{
public override bool IsValid(object value) //how do I get the other value in here?
{
//validation logic here
return base.IsValid(value);
}
}
I appreciate any help!
There is already a comparison validation attribute built into mvc. See the documentation here:
http://msdn.microsoft.com/en-us/library/system.web.mvc.compareattribute(v=vs.98).aspx
An example of use would be:
public string Password { get; set; }
[Compare("Password", ErrorMessage = "Uh oh")]
public string PasswordAgain { get; set; }
You can create custom attributes and set additional information to their public properties.
public class CustomValidationAttribute : ValidationAttribute
{
public string MeaningfulValidationInfo { get; set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// do whatever meaningful with MeaningfulValidationInfo
return base.IsValid(value, validationContext);
}
}
You'd set the additional info this way:
[CustomValidationAttribute(MeaningfulValidationInfo = "blah")]
public ActionResult Index()
{
return View();
}
If you are trying to check if both entered passwords were identical, you can simply validate that in your model.
public class LoginModel
{
[Required]
[EmailAddress]
public string EmailAddress { get; set; }
[Required]
public string Password { get; set; }
[Required]
[Compare("Password")]
[Display(Name = "Confirm password")]
public string ConfirmPassword { get; set; }
}
}
Compare annotation is the easiest option for this. As you can see below, Compare points to the Password attribute.
[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; }
You could use the *Compare Validate * control inbuilt on the ASP.NET Tools
I've provided a sample below
<body>
<form id="form1" runat="server">
<div>
<asp:Label
id="lblBirthDate"
Text="Birth Date:"
AssociatedControlID="txtBirthDate"
Runat="server" />
<asp:TextBox
id="txtBirthDate"
Runat="server" />
<asp:CompareValidator
id="cmpBirthDate"
Text="(Invalid Date)"
ControlToValidate="txtBirthDate"
Type="Date"
Operator="DataTypeCheck"
Runat="server" />
<br /><br />
<asp:Button
id="btnSubmit"
Text="Submit"
Runat="server" />
</div>
</form>
</body>
Please refer to any of the links below to widen your knowledge
http://www.java2s.com/Tutorial/ASP.NET/0160__Validation/CompareValidatorperformsthreedifferenttypesofvalidations.htm
http://www.vkinfotek.com/aspnetvalidationcontrols.html
Related
As we know when you create an ASP.NET Core appp using Individual User Authentication project template, it creates a default ResetPassword.cshtml view. In that View I need to set logged in user name input tag as readonly. But doing so is throwing the following validation error. If I don't make it readonly the below screen successfully allows user to change password.
Question: Why the following validation error on form submit - when the UserName input tag is set to readonly? I know that if the input tag is disabled then form submit does not submit the input tag's value (also explained by #AdamBellaire here). It seems [Required] annotation in public string UserName { get; set; } is somehow conflicting with readonly attribute of input tag.
public class ResetPasswordViewModel
{
[Required]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.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; }
}
UPDATE
ResetPassword.cshtml: [It's a default View created by VS2017. Only change is that I added readonly attribute in input tag below]
#model ResetPasswordViewModel
...
<input asp-for="#User.Identity.Name" class="form-control" readonly />
...
In this situation, it really makes no sense to expect #User.Identity.Name in the view to bind to UserName in the view model.
It would seem that the code the IDE generated is wrong. Maybe a messed up scaffolding template somewhere, who knows.
You need to change your asp-for to equal UserName. See below.
<input asp-for="UserName" class="form-control" readonly />
Glad this helped!
The situation is that I have a complex model with a lots of data to view and aside to it, control panel with for example password change.
One big model with another property model which will be submitted.
The information inside the big model requires loading and is not required upon POSTing
The Model
public class ProfileModel {
// This is the submitted model:
public PasswordChangeModel Password = new PasswordChangeModel();
// Personal Info
public string Name {get; set;}
public string LastName {get; set;}
// 15~ more fields
}
The password model w/ validation
public class PasswordChangeModel {
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "OldPassword")]
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 = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Repeat password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string RepeatPassword { get; set; }
}
Controller Catching-Action
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult ChangePassword(PasswordChangeModel model) {
if (!ModelState.IsValid) //validate the model
return View(model);
//do stuff ...
return Index();
}
The Html to generate the form
<form asp-controller="Profile" asp-action="ChangePassword" asp-antiforgery="true">
<div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>
<label asp-for="Password.OldPassword">Old Password</label>
<input asp-for="Password.OldPassword"/>
<label asp-for="Password.Password">New Password</label>
<input asp-for="Password.Password"/>
<label asp-for="Password.RepeatPassword">New Password Repeat</label>
<input asp-for="Password.RepeatPassword"/>
<input type="submit" class="btn" name="submit" value="Change"/>
</form>
The Question
Now after reviewing the code, my question is - is it possible to submit it that way, if not whats the most convenient and clean way to do it.
Note: I always can just include 3 fields inside the model ProfileModel of the password changing but A-It's ugly and B-It still makes the entire ProfileModel data to load.
I would say that the cleanest way to do this is to have a separate update password view. This or switching to a ajax post so you can post without reloading the page. If you can't make a model that could do a roundtrip to the server without repopulating it then don't do standard form posting. It can be done but when I've seen it usually there are subtle errors when rerendering the page on validation error.
It's just easy to shoot yourself in the foot.
This is what I've ended up doing.
Worked just fine.
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult ChangePassword([Bind(Prefix = "Password.OldPassword")]string Password_OldPassword,
[Bind(Prefix = "Password.Password")] string Password_Password,
[Bind(Prefix = "Password.RepeatPassword")] string Password_RepeatPassword) {
//Change the password
}
Bind attribute redirected the value of Password.OldPassword to Password_OldPassword.
In my MVC-project I have different custom validation-attributes. One of them is to check the value of a property against the value of another property.
As stated in many articles, I add something like
result.ValidationParameters.Add("otherproperty", _otherPropertyHtml);
result.ValidationParameters.Add("comparetype", _compareType);
result.ValidationParameters.Add("equalitytype", _equalityType);
to the returning ModelClientValidationRule object.
My problem now is, that - if my property to check - is encapsulated in another object, validation will not work.
If I create something like
#Html.TextBoxFor(m => m.ValueOne)
#Html.TextBoxFor(m => m.ValueTwo)
validation will work fine as it renders
data-val-otherproperty="ValueTwo"
My problem is for the following
#Html.TextBoxFor(m => m.IntermediateObject.ValueOne)
#Html.TextBoxFor(m => m.IntermediateObject.ValueTwo)
This will render two textboxes with names IntermediateObject_ValueOne and IntermediateObject.ValueTwo. But still data-val-otherproperty="ValueOne" for the first textbox.
How can it be achieved, that data-val-otherproperty has always the correct name of the other property?
My thoughts are something like HtmlHelper<>.NameFor(m => ...) or something that uses reflection?
Update 1 - Added code as requested by comments
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, AllowMultiple = false)]
public class CustomCompareToOther : ValidationAttribute, IClientValidatable
{
// private backing-field
private readonly string _otherPropertyName;
// constructor
public OemCompareToOther(string otherPropertyName)
{
_otherPropertyName = otherPropertyName;
}
// implementation of IClientValidatable
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var result = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.DisplayName),
ValidationType = "customcomparetoother"
};
// add the property-name so it is known when rendered for client-side validation
result.ValidationParameters.Add("otherproperty", _otherPropertyHtml); // here I would need IntermediateObject.ValueTwo instead of only ValueTwo
yield return result;
}
}
Usage at model-level would be
public class MyModel
{
[CustomCompareToOther("ValueOTwo", CompareType.NotEqual, PropertyType.String)]
public string ValueOne { get; set; }
[CustomCompareToOther("ValueTwo", CompareType.NotEqual, PropertyType.String)]
public string ValueTwo { get; set; }
}
And what I will put into my View would be something like
public class ViewModel
{
public MyModel IntermediateObject { get; set; }
}
used e.g. return View(new ViewModel()).
So, in the rendered HTML I would have an input
<input type="text" name="IntermediateObject_ValueOne" id="IntermediateObject.ValueOne" data-val-customcomparetoother-otherpropertyname="ValueTwo" />
<input type="text" name="IntermediateObject_ValueTwo" id="IntermediateObject.ValueTwo" data-val-customcomparetoother-otherpropertyname="ValueOne" />
but I need
<input type="text" name="IntermediateObject_ValueOne" id="IntermediateObject.ValueOne" data-val-customcomparetoother-otherpropertyname="IntermediateObject.ValueTwo" />
<input type="text" name="IntermediateObject_ValueTwo" id="IntermediateObject.ValueTwo" data-val-customcomparetoother-otherpropertyname="IntermediateObject.ValueOne" />
in the html so javascript-validation can fetch the other property correctly.
You can use the [Compare("PropertyName")] Data Annotation.
Example in your View Model:
[Display(Name = "New Password")]
[DataType(DataType.Password)]
public string NewPassword { get; set; }
[Display(Name = "Confirm Password")]
[DataType(DataType.Password)]
[Compare("NewPassword")]
public string PasswordConfirmation { get; set; }
Just remember to add the System.ComponentModel.DataAnnotations namespace to your using statements
I'm making a website with MVC5 ASP.NET.
I'm using Identity framework 2.0 implement class with properties such as passwordhash, username, email, emailconfirmed and so on. I'm using userManager.ChangePassword(user.Id, Oldpassword, Newpassword);, but i can't figure out how i should get a password from a user as plain text (string)
[HttpPost]
public ActionResult ChangePassword(AspNetUsersViewModel userView)
{
UserManager<IdentityUser> userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>());
var result = userManager.ChangePassword(_User.Id, "123456789", userView.Password);
return RedirectToAction("Index", "ConfigUser");
}
As now I'm i have hardcoded users current password "123456789" to test if it works, and it does.
I hope you guys can help.
Add password input to the View inside the form tag
<input type="password" id= "userNewPassword" name="userNewPassword">
Pass the userNewPasswor as string to the controller after the userView and the pass it to the UserManager
[HttpPost]
public ActionResult ChangePassword(
AspNetUsersViewModel userView,
string userNewPassword){
UserManager<IdentityUser> userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>());
var result = userManager.ChangePassword(_User.Id, userNewPassword , userView.Password);
return RedirectToAction("Index", "ConfigUser");
}
Note: the Best way is to Modify the userView and add the userNewPassword to the model
Update:
in the visual studio 2013 the if you used the asp.net default template you will find the flowing class
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; }
[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; }
}
I am trying to set-up a remote validation similar to the one in this example:
Example
My application has a twist however, my form elements are dynamically generated, therefore this tag:
[Remote("doesUserNameExist", "Account", HttpMethod = "POST", ErrorMessage = "User name already exists. Please enter a different user name.")]
is not set in stone, I need to vary the ErrorMessage for example and preferably vary the action. Is it possible, or would you suggest taking the long-way, meaning to implement the whole ajax validation on my own.
Any suggestions are appreciated.
If you need to have a dynamic error message then you could return this as string from your validation action:
public ActionResult DoesUserNameExist(string username)
{
if (Exists(uasername))
{
string errorMessage = "Some dynamic error message";
return Json(errorMessage, JsonRequestBehavior.AllowGet);
}
return Json(true, JsonRequestBehavior.AllowGet);
}
And if you need even more flexibility such as invoking dynamic dynamic actions, then you're better of rolling your custom validation solution instead of relying on the built-in Remote attribute.
You can inherit from RemoteAttribute and make it fetch the required values from a service or factory according to your own logic. Here is an example:
[AttributeUsage(AttributeTargets.Property)]
public class MyRemoteAttribute : RemoteAttribute
{
public MyRemoteAttribute(Type type, string propertyName)
: base(MyRemoteAttributeDataProvider.GetAttributeData(type,propertyName).Action, MyRemoteAttributeDataProvider.GetAttributeData(type,propertyName).Controller)
{
var data = MyRemoteAttributeDataProvider.GetAttributeData(type,propertyName);
base.ErrorMessage = data.ErrorMessage;
base.HttpMethod = data.HttpMethod;
}
}
public static class MyRemoteAttributeDataProvider
{
public static RemoteAttributeData GetAttributeData(Type type
, string propertyName)
{
//this is where you are going to implement your logic im just implementing as an example
//you can pass in a different type to get your values. For example you can pass in a service to get required values.
//property specific logic here, again im going to implement to make this
//specification by example
var attrData = new RemoteAttributeData();
if(propertyName == "MyOtherProperty")
{
attrData.Action = "MyOtherPropertyRelatedAction";
attrData.Controller = "MyOtherPropertyRelatedController";
attrData.ErrorMessage = "MyOtherPropertyRelated Error Message";
attrData.HttpMethod = "POST";
}
else
{
attrData.Action = "UserNameExists";
attrData.Controller = "AccountController";
attrData.ErrorMessage = "Some Error Message";
attrData.HttpMethod = "POST";
}
return attrData;
}
}
public class RemoteAttributeData
{
public string Controller { get; set; }
public string Action { get; set; }
public string HttpMethod { get; set; }
public string ErrorMessage { get; set; }
}
And this is how you are supposed to use is:
public class RegisterViewModel
{
[Required]
[Display(Name = "User name")]
[MyRemote(typeof(RegisterViewModel),"UserName")]
public string UserName { 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")]
[System.ComponentModel.DataAnnotations.Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[Required]
[MyRemote(typeof(RegisterViewModel),"MyOtherProperty")]
public string MyOtherProperty { get; set; }
}
As I also mentioned above at the commentary. You should specialize that provider according to your needs.
I hope this helps.
UPDATE:
I update the implementation based on your comment, so that it takes a property name and does some property name specific wiring.