Can the built in ASP MVC validation be made to behave differently for different actions of a same controller ? For example I have a user controller and it has actions like create, edit and other actions. So in model user the attribute Username is being validated for its uniqueness. If there is an user present with the same username, it throws and error username already present. So using the same validator for edit action throws an error "username already present" while editing an user. Can anybody tell me if there is a way to do solve this problem? I am pasting my validator code for reference.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace Models
{
[MetadataType(typeof(AdmiUserMetadata))]
public partial class AdminUser
{
public class AdmiUserMetadata
{
[Required(ErrorMessage = "Required Field")]
public string Id { get; set; }
[Required(ErrorMessage = "Required Field")]
[RegularExpression("[\\S]{6,}", ErrorMessage = "Must be at least 6 characters.")]
[Username(ErrorMessage = "Username already taken")]
public string Username { get; set; }
[Required(ErrorMessage = "Required Field")]
[RegularExpression("[\\S]{6,}", ErrorMessage = "Must be at least 6 characters.")]
public string Password { get; set; }
[Required(ErrorMessage = "Required Field")]
public string Name { get; set; }
[Required(ErrorMessage = "Required Field")]
[RegularExpression("^[a-z0-9_\\+-]+(\\.[a-z0-9_\\+-]+)*#[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2,4})$", ErrorMessage ="Invalid E-mail ID")]
public string Email { get; set; }
[Required(ErrorMessage = "Required Field")]
[RegularExpression("(Active|Disabled)", ErrorMessage = "Select the status of User")]
public string Status { get; set; }
[Required(ErrorMessage = "Required Field")]
[RegularExpression("^[1-9]", ErrorMessage = "Select the group of User")]
public string Group { get; set; }
}
}
public class UsernameAttribute : ValidationAttribute
{
IUserRepository _repository = new UserRepository();
public override bool IsValid(object value)
{
if (value == null)
return true;
if (_repository.IsUsernamePresent((string)value))
{
return false;
}
return true;
}
}
}
What you are validating is a business rule.
No two users can have the same username.
I would have a User service that enforces this rule on creation/edit. Attributes are best suited for input validation. (eg Is the integer non-negative? A valid email address? etc)
I don't see how this can be done if a class has attributes that determines validation. This obviously works for most projects, but for me this is also not working out.
If you need to attach different sets of validation rules check out http://fluentvalidation.codeplex.com/. I tried it and liked it.
It doesn't handle client validation. I dropped that because I have ajax calls in most parts and that feels a bit like client validation.
Related
I get an error when inserting data into my database. My database columns are:
username, password
and I added confirmpassword to the model to extend it.
How do I get rid of the confirmpassword to insert the username and password while using the model with confirmpassword property?
My code:
BootstrapTrainingEntities db = new BootstrapTrainingEntities();
var u = new User();
u.username = user.username;
u.password = user.password;
// how to I remove or ignore the confirmPassword property when saving?
db.Users.Add(u);
db.SaveChanges();
Model
[MetadataType(typeof(metadataUser))]
public partial class User
{
public string confirmPassword { get; set; }
}
public class metadataUser
{
[Required(ErrorMessage = "Username is required", AllowEmptyStrings = false)]
[Display(Name ="Username")]
public string username { get; set; }
[Required(ErrorMessage ="Password is required", AllowEmptyStrings = false)]
[Display(Name = "Password")]
[DataType(DataType.Password)]
public string password { get; set; }
[Required(ErrorMessage ="Confirmation Password is required", AllowEmptyStrings = false)]
[Display(Name ="Confiramation Password")]
[DataType(DataType.Password)]
[Compare("password",ErrorMessage = "Password does not match")]
public string confirmPassword { get; set; }
}
This SO has three different solutions that may help you:
How not persist property EF4 code first?
Summary:
First try the DataAnnotations approach:
Make sure you include the required library. Then apply the [Not Mapped] annotation to your field in the model.
using System.ComponentModel.DataAnnotations;
[NotMapped]
public string confirmPassword { get; set; }
If this doesn't do it, try to modify your OnModelBuilding method in your dbContext. There are two options in this block. The first is to use the Ignore method.
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MetaDataUser>().Ignore(p => p.confirmPassword);
}
}
The second is to manually remap the model building and exclude your field.
Additional SO answers that may be useful:
Entity Framework Code First - How to ignore a column when saving
Exclude a field/property from the database with Entity Framework 4 & Code-First
MS Doc on how to manually map properties to db fields:
https://learn.microsoft.com/en-us/ef/core/modeling/relational/columns
Clean example of answer provided:
http://www.dotnetodyssey.com/2015/03/31/ignore-class-property-to-table-column-in-entity-framework-code-first/
I think what you need to do is add a viewmodel for the page.First of all you will have model that is generated from entity framework which might look as below.
public class User
{
public int id{get;set;}
public string username {get;set;}
public string password {get;set;}
}
So now create a viewModel for your View .This might look as below.
public class UserViewModel
{
public int id{get;set;}
[Required(ErrorMessage = "Username is required", AllowEmptyStrings = false)]
[Display(Name ="Username")]
public string username { get; set; }
[Required(ErrorMessage ="Password is required", AllowEmptyStrings = false)]
[Display(Name = "Password")]
[DataType(DataType.Password)]
public string password { get; set; }
[Required(ErrorMessage ="Confirmation Password is required", AllowEmptyStrings = false)]
[Display(Name ="Confiramation Password")]
[DataType(DataType.Password)]
[Compare("password",ErrorMessage = "Password does not match")]
public string confirmPassword { get; set; }
}
and now in your controller action method. you can do as below.
[HttpPost]
public ActionResult AddUser(UserViewModel model)
{
User user=new User();
user.username=model.username;
user.password=model.password;
db.User.Add(user);
}
Hope it helps !
Hi I am using entity framework code first approach for my project.
i have a class called Login as shown below
public class Login
{
[Required(ErrorMessage = "UserName Required")]
[DisplayName("Username")]
[Key]
public string Username { get; set; }
[DataType(DataType.Password)]
[Required(ErrorMessage = "Password Required")]
[DisplayName("Password")]
public string Password { get; set; }
[Required(ErrorMessage = "Email Id Required")]
[DisplayName("Email ID")]
[RegularExpression(#"^\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*$",
ErrorMessage = "Email Format is wrong")]
public string Email { get; set; }
}
My database context is as below
public class ContextDB:DbContext
{
public DbSet<Login> LoginModel { get; set; }
}
The table created in the database is Logins.
In my view the validation messages are not working.
Can anyone please help?
This might sound stupid bud are u sure that you are passing right class to your Login ActionResult, not some LoginViewModel or similar stuff? I know that by default some preloaded models exist, so make sure that this isnt case.
I'm trying to make Json .NET Ignore a property by using the Json Ignore Attribute when clients GET the object but I want to be able to receive that property when a client is POST'ing
In example I have:
When the client POSTs data, password should be sent:
{"email":"email#domain.com","password":"P#ssW0rd1!","firstname":"Joe","lastname":"Doe"}
However, when the client GETs the same object, I should Ignore the Password:
{"email":"email#domain.com","firstname":"Joe","lastname":"Doe"}
Class:
public class User
{
public User()
{
this.JoinDate = DateTime.UtcNow;
this.IsActive = false;
}
public int Id { get; set; }
[Required(ErrorMessage = "Email is required!")]
public string Email { get; set; }
[JsonIgnore]
public string HashedPassword { get; set; }
[Required(ErrorMessage = "Password is required!")]
public string Password { get; set; }
public DateTime JoinDate { get; set; }
[Required(ErrorMessage = "First Name is required!")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Last Name is required!")]
public string LastName { get; set; }
}
Any ideas, suggestions, comments???
In your scenario here, I would recommend you to split your User class into 2 separate model classes:
Login class, which has the login information (i.e. Email, Password)
UserInfo class, which has the rest of the metadata about the User (i.e. FirstName, LastName)
This way, we are not depending on the serializer to hide sensitive data.
You could use [IgnoreDataMember] attributes and the out-of-box XML and JSON formatters will support them, but there is no guarantee that any other custom formatter registered will support it.
Note that [JsonIgnore] is only supported in the JSON formatter but not the XML formatter.
For the edition of my user, I've to ensure that password and the repeat password are the same. I found the "Compare" validator, but I cant make it work.
my model looks like the following:
public class UserEditionViewModel{
[Compare("User.Password")]
public String RepeatPassword{get;set;}
public User User {get;set;}
public List<Language> AvailableLanguages{get;set;}
public List<Country> AvailableCountries{get;set;}
}
and the User model:
public class User{
[Required]
public String Name{get;set;}
//lot of other properties omitted...
[RegularExpression(#"(|.*(?=.{6,})(?=.*\d)(?=.*[a-zA-Z]).*)", ErrorMessageResourceType = typeof(LocalizationResources.Views.User.Edition), ErrorMessageResourceName = "InvalidPassword")]
//And I've localization attributes
public String Password{get;set;}
}
In the view I only have something like:
#Html.PasswordFor(m=>m.User.Password)
#Html.PasswordFor(m=>m.RepeatPassword)
But I ever get this error, even if the two items are matching:
'Password repeat' and 'User.Password' do not match.
I also got this error when I'm doing the client validation.
For me the most obvious error is that it can't found the subproperty. Am I right? If yes, how to avoid this behavior. If no, what can be the problem???
A workaround would be to create another property on the UserEditionViewModel that reads and writes to the inner Userclass.
public String UserPassword
{
get
{
return User.Password;
}
set
{
User.Password = value;
}
}
And then bind your controls to that property instead, and change the [Compare("User.Password")] to [Compare("UserPassword")]. I'm not really sure if it can be done any other way short of writing your own custom validator.
I had a similar problem and ended up writing my own validator for this which turned out surprisingly complex since you can have any layer of inheritance to get to your property. If there is another solution, I'd be equally happy to know about it.
You can try this which worked for me..
In your project -> References-> right click->Manage NuGet Packages..
install DataAnnotationsExtensions package.
Then validate your model as follows:
public class Employee
{
[Required(ErrorMessage="Name field Required")]
public string name { get; set; }
[Required(ErrorMessage = "Name field Required")]
public string email { get; set; }
[Required(ErrorMessage = "Depatrment field Required")]
public string department { get; set; }
[Required(ErrorMessage = "Designation field Required")]
public string designation { get; set; }
public string phone { get; set; }
[Required(ErrorMessage = "Password field Required")]
[Display(Name="Password")]
public string password { get; set; }
[Required(ErrorMessage="Confirm password")]
[Display(Name="Re-type Password")]
[EqualToAttribute("password",ErrorMessage="Password miss-match")]
public string Re_Password { get; set; }
}
That's it
My Question is
As I am passing UserCreateViewModel from my Create Controller that means my Validation(ModelState.IsValid) will work only
on UserCreateViewModel if Annotation are defined on it. But I can not define DataAnnotation on each of my ViewModels because that will be alot of work.
Instead I want to put it on User domain model. So how do I fix Create method to fix as my Annotation work and mapper too without adding more code to controller.
//My Controller Create Method
[HttpPost]
public ActionResult Create(UserCreateViewModel user)
{
if (ModelState.IsValid)
{
var createUser = new User();
Mapper.Map(user, createUser);
_repository.Add(createUser);
return RedirectToAction("Details", new { id = createUser.UserId });
}
return View("Edit", user);
}
//UserCreateViewModel -> Create Specific view model
public class UserCreateViewModel
{
public string UserName { get; set; }
public string Password { get; set; }
}
//User -> Domain Object
[MetadataType(typeof(User.UserValidation))]
public partial class User
{
private class UserValidation
{
[Required(ErrorMessage = "UserName is required.")]
[StringLength(50, MinimumLength = 2, ErrorMessage = "{0} is between {1} to {2}")]
[RegularExpression(#"(\S)+", ErrorMessage = "White space is not allowed")]
public string UserName { get; set; }
[Required(ErrorMessage = "Password is required.")]
[StringLength(50, MinimumLength = 2, ErrorMessage = "{0} is between {1} to {2}")]
public string Password { get; set; }
}
}
Validation should be put at least on the view model because this is what you receive as user input. As far as validation on the model is concerned you could add it as well but as long as you are passing a view model to your POST action (which is exactly what you should do) the validation on the model will be ignored. Of course that's not a problem here because a model is something that might be reused on other applications as well which don't use view models and this way your model is guaranteed to be valid. As far as ASP.NET MVC is concerned this step is not necessary.