I have a method that validates a number, I don't have a regular expression for this as it is kind of complicated to create.
public bool IsRegistrationNumberValid(int number)
{
...
}
On my form, I have a textbox and I want to add validation for this column.
How can I create a custom annotation or hook into the ModelState object to add an error message?
My POST controller action is like:
[HttpPost]
public ActionResult Create(UserRegistrationViewData model)
{
if (ModelState.IsValid)
{
...
}
}
I'm not sure what options I have, can I just create a custom attribute to add to my model?
And/Or should i just hook into the Model state and add the error message before I check for ModelState.IsValid?
There are a few approaches to this and the best one for you will depend on the following:
Where your IsRegistrationNumberValid method is located and whether the the logic be moved?
Are you validating the integrity of the user input or the domain (you should be checking both, but the validation for each will be in a different place)?
Personal preference.
The way I see it you have the following options available:
Validate in your controllers action methods.
Validate using the IValidatableObject interface.
Use a custom ValidationAttribute.
Validate in your service layer.
Option 1: Validate in your controller:
First of all you could simply validate the value in your controllers action method and update the ModelState like so:
[HttpPost]
public ActionResult Create(UserRegistrationViewData model)
{
if (ModelState.IsValid)
{
if (!someObject.IsRegistrationNumberValid(model.value))
{
ModelState.AddModelError("PropertyName", "There is an error..");
Return View()
}
else
{
// Carry out successful action here...
}
}
}
Option 2: use the IValidatableObject interface.
A second, much cleaner way is to implement the IValidatableObject interface on your viewModel so that you can move the logic out of the controller:
public class ViewModel : IValidatableObject
{
public int Value { get; set; }
IEnumerable<ValidationResult> IValidatableObject.Validate(ValidationContext validationContext)
{
if (!staticClass.IsRegistrationNumberValid(this.Value))
{
yield return new ValidationResult("An error occured");
}
}
Option 3: Create a custom validation attribute.
As mentioned you could create a custom validation attribute by deriving from ValidationAttribute as shown in this article.
The choice between the IvalidatableObject interface and a custom validation attribute is usually down to preference, however, one case where the IValidatableObject interface wins is when your validation depends on multiple properties (E.G. checking if one date is after another).
Option 4: Validate in your service layer.
Finally, if your validation is dependant on other information from a database you might want to take a look at this tutorial on validating with a service layer. The article is not perfect (the service and controller are a bit too tightly coupled) but is a good start and with a few modifications you can pass database validation errors (such as primary key violations) into your user interface in a very transparent and user-friendly way.
You will probably end up using a mixture of options 2, 3, and 4. You don't really want to be using the first option if possible as it makes your controller methods more complicated and makes it more difficult to reuse validation logic elsewhere.
My advice would be the following:
If you are validating the integrity of the user input (E.G. checking a date is in the correct format) use a mixture of the IValidatableObject interface and the ValidationAttribute classes.
If you are validating the integrity of the domain (ensuring no duplicate entities are entered, or that relationships between entities are defined) carry out the validation in the service layer.
Related
I use Data Annotations to validate my Web API 2 models. For basic attribute-based validation (Required, Range, etc.) it's pretty easy to provide localized messages by injecting custom ModelMetadataProvider. However, for more complex rules I implement IValidatableObject which returns a sequence of ValidationResult:
public class ValidationResult
{
public ValidationResult(string errorMessage);
public ValidationResult(string errorMessage, IEnumerable<string> memberNames);
}
It looks like there is no way to specify ErrorMessageResourceName here. And I do not want to make my models dependent on the localization provider. How can I solve the problem?
It can happen if IValidatableObject.Validate method is called before the culture is available to the system. If the Validate method is called manually from the controller action, the error messages are properly localized.
Where you are setting the culture? you need to set it in Controller > ExecuteCore. Please have a look at this post or This post may help.
I have several fields that only need to be displayed depending on selections in other fields. When these fields are displayed, they are also required. First, I tried to just hide the field in the view. But, the field is set to be required in the view model, so the ModelState comes back as invalid since the field is required. For now, I am creating several different view models to handle all the different possibilities, but this is getting cumbersome since there are dozens of variations for the form in the view. Is there some better way to get the results I need? For example, could I use just one model with all the fields there, then hide them in the view, but only make them required when they need to be displayed? Maybe I could add the data annotation in the ActionResult in the controller dynamically?
I found another post that might be the same question here: ASP.NET MVC 3: programmatically add DataAnnotation (RequiredAttribute) to view model
You can implement IValidatableObject. This way you can get conditional validation on your model properties.
public class MyViewModel : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Condition here)
{
yield return new ValidationResult("Validation error");
}
if (Other Condition here)
{
yield return new ValidationResult("Other Validation error");
}
}
}
Another option is MVC Foolproof Validation.
I have a model class :
public class YearlyChageRate
{
public int Year { get; set; }
public double Rate { get; set; }
}
and I want to check that Yeae is unique or no and in condition Year is not unique application show an error message to users.How can I check the Year filed is repeated or not?
Here is a good example:
http://tugberkugurlu.com/archive/asp-net-mvc-remote-validation-for-multiple-fields-with-additionalfields-property
And here too: MVC validation for unique
You can use Remote attribute in your model to perform check for unique value in database.
This is official example of Remote attribute: http://msdn.microsoft.com/en-us/library/gg508808(v=vs.98).aspx
And one more: http://www.a2zdotnet.com/View.aspx?Id=198
You could use the [Remote] validation attribute on your view model.
Although you can use DataAnnotations attributes for validation and the [Remote] attribute for checks against the DB, it's not a very good design choice.
Let me explain:
data access is a data-layer matter
validation is a business-layer matter
user input and feedback is a ui matter
With DataAnnotations, you're mixin 3 in 1. It can be faster, but surely not well designed.
You could try a more disciplinate approach, like this:
Have a method at business level that will take your object as a parameter, perform validation internally using a validation framework of your choiche;
This method will call the data access to persist the object only if the validation passed;
This method will always return to the UI the validated object, plus a collection of fields/errors if anything didn't validate;
When you read the output of the method in your ui, you can either display a success page if there were no errors, or redisplay the form with the validation errors returned. To do this, the use of the PRG pattern is highly recommended, as you should never display a page on a POST method. Google for the PRG pattern to learn more about it. MvcContrib has a nice ActionFilter called ModelStateToTempData to make the implementation of the PRG pattern something trivial.
I am building an ASP.NET MVC application, and I am trying to find a way where I can collect data from the user in a custom view model, try and set these values to one or more of my entities, then based on validation logic on those entities, collect error messages if any and get them back to the view. I am new to MVC and web design in general, it is therefore quite possible that I am making major conceptual errors, but I have tried to research as far as I could.
I realize that this is more work than having the view be strongly typed to the entity, where it would then be easy to have the validation errors display, as in this tutorial. However, I don't want to do this for security and because there are some places where I want to have values collected from a single view model to be set in multiple different entities.
I also realize that I could set validation rules on the view model itself, rather then on the entity, but this seems like poor architecture, as I would have to define them redundantly in different view models, and I would then be less sure whether I had prevented bad values from being persisted to the database.
My plan is therefore to have the validation rules be set on the entity itself and to have the view model as a dumb container. Then, in a different location in the application, I would apply the values from the view model to my entity(ies) in accordance my business logic. At this point, I would like my validation logic to be called. If the data is invalid, I plan on setting the error string in the custom attribute on the view model to the error from the validation logic on the entity. I am thinking it would go something like this:
public class CustomViewModel()
{
[SomeCustomValidation()] //Has a place for an error string and a boolean IsValid
String Property { get; set; }
}
public class BusinessLogic()
{
CustomViewModel TestForValidity(CustomViewModel viewModel)
{
MyEntity.Property = viewModel.Property;
// if(MyEntity.IsValid)? catch SomeSortOfException?
// collect error message, put it in the attribute on the view model, set IsValid to false
}
}
public class MyEntity()
{
[MoreCustomValidation()]
public String Property { get; set; }
}
I therefore have three questions:
When I try to pass data that does not satisfy my validation rules, will some sort of error or exception be thrown? Is there some indication I can use or collect when I try to pass in invalid data?
If there is some error or exception thrown, how can I collect the error message so I can assign it to my view model?
Most importantly, am I going about this all wrong? Can attributes not be modified at runtime, for example to include a new error message or to change IsValid to false? I know that I can use reflection to access the attributes. If I can modify them, how would I do so?
Thank you in advance for your help. I apologize if I misunderstand something big.
It seems you might be over-complicating things a bit. I think what you want to do is prevent the model binder from binding to properties that it should not be able to, as well as retaining the ability to check ModelState.IsValid when properties on your model do not meet the requirements of their validation attributes.
IMO the best way to accomplish this is through the use of what I call "strongly-typed binding filters". First define an interface with only the properties that you want the model binder to be allowed to bind on your model.
public interface INewBlogPost
{
string Title { get; set; }
string Body { get; set; }
}
Then ensure your entity inherits from the binding filter interface.
public class BlogPost : INewBlogPost
{
...
}
Next, modify your action method to create a new entity and manually invoke the model binder whilst typing it to the interface you just defined.
public ActionMethod NewBlogPost()
{
BlogPost newBlogPost = new BlogPost();
TryUpdateModel<INewBlogPost>(newBlogPost);
if (ModelState.IsValid)
{
...
}
}
Because you passed in a type when invoking the model binder via TryUpdateModel you explicitly told the model binder what type to bind to. This means that the model binder will only have access to the properties listed in the interface. Now when you pass a model into the method to be bound it will have to be of type INewBlogPost. Because your entity inherits from your binding filter interface, an instance of it will satisfy this requirement. The model binder will happily bind to the properties on the interface completely oblivious of any other properties your model object may have.
See this blog post for more information.
Aside
It is sometimes easy to run into action method ambiguity when you have two action methods with the same name; one for POST and one for GET like this:
[HttpGet]
public ActionResult NewBlogPost()
{
return View();
}
[HttpPost]
public ActionResult NewBlogPost()
{
BlogPost newBlogPost = new BlogPost();
TryUpdateModel<INewBlogPost>(newBlogPost);
if (ModelState.IsValid) { ... }
}
An easy way to fix that is to modify your POST action method to look like this:
[HttpPost]
public ActionResult NewBlogPost(FormCollection formCollection)
{
BlogPost newBlogPost = new BlogPost();
TryUpdateModel<INewBlogPost>(newBlogPost, formCollection);
if (ModelState.IsValid) { ... }
}
The MVC model binder knows how to bind the request form collection to an argument of type FormCollection so it will populate this just fine. Because your POST action now accepts an argument, it is no longer ambiguous with your GET method. You can pass this formCollection into TryUpdateModel to be used as the binding source if you wish, but you don't have to as it will default to the request form collection anyway. But since you are passing it in you may as well use it :)
A name field needs to be verified as starting with a capital letter and not being allowed to take a number. I'll do this with regular expressions. Implementing the MVC pattern.
Should I have the controller send the value that was input to a checking class and send corresponding error msg's back to the UI and after checking then call the class that writes it to the DB
OR
should I have the controller send the 'value input' to the class that writes it to the DB and this method then calls the validation method?
you can use something like this (i've used for email validation)
[Required(ErrorMessageResourceType = typeof(CCSModelResources), ErrorMessageResourceName = "ANTCommonTextRequiredMessage")]
[RegularExpression(#"^[a-zA-Z][\w\.-]*[a-zA-Z0-9]#[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$",
ErrorMessageResourceType = typeof(CCSModelResources), ErrorMessageResourceName = "ANTCommonTextRegularExpressionMessage")]
public new string EmailAddress
{
get { return base.EmailAddress; }
set { base.EmailAddress = value; }
}
and you controller code like
[Authorize]
[HttpPost]
public ActionResult UpdatePersonalDetails(FormCollection form)
{
regUserWizard.PersonalDetails = new MVCPersonalDetails();
if (!TryUpdateModel<MVCPersonalDetails>(regUserWizard.PersonalDetails, form.ToValueProvider()))
{
return View("UpdateUser", regUserWizard);
}
else
{
//you code
}
return RedirectToAction("Index", "Home");
}
you view code like
< %= Html.ValidationSummary("Account creation was unsuccessful. Please correct the errors and try again.") %> <% Html.EnableClientValidation();
using (Html.BeginForm()) { %>
//you code
The first approach seems more correct because the db access logic shouldn't be mixed with validation.
THe purpose of the controller is to validate input and provide valid input to models. Your model should not be concerned with what inputs is provided by the views. It should concentrate on Business logic only. You can add some validation code on the client side for ease-of-use as well but it needs to be present on server side too for security purpose.
Your 1st approach is correct...go ahead with it...
In my opinion, it all depends on what kind of validation you want to perform:
1. If you don't want a field to be empty, I will do that check on the view layer. This is where most regex could be applied.
2. If I want to ensure that a user input(, say a username) is unique or not , I will do that validation on the controller side and pass any feeback back to the view. In the latter, the controller might have a dependency on an abstraction of a data access layer or service layer to do the actual checking.