I'm using ASP.NET MVC 3 code-first and I have added validation data annotations to my models. Here's an example model:
public class Product
{
public int ProductId { get; set; }
[Required(ErrorMessage = "Please enter a name")]
public string Name { get; set; }
[Required(ErrorMessage = "Please enter a description")]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Required(ErrorMessage = "Please provide a logo")]
public string Logo { get; set; }
}
In my website I have a multi-step process to create a new product - step 1 you enter product details, step 2 other information etc. Between each step I'm storing each object (i.e. a Product object) in the Session, so the user can go back to that stage of the process and amend the data they entered.
On each screen I have client-side validation working with the new jQuery validation fine.
The final stage is a confirm screen after which the product gets created in the database. However because the user can jump between stages, I need to validate the objects (Product and some others) to check that they have completed the data correctly.
Is there any way to programatically call the ModelState validation on an object that has data annotations? I don't want to have to go through each property on the object and do manual validation.
I'm open to suggestions of how to improve this process if it makes it easier to use the model validation features of ASP.NET MVC 3.
You can call the ValidateModel method within a Controller action (documentation here).
ValidateModel and TryValidateModel
You can use ValidateModel or TryValidateModel in controller scope.
When a model is being validated, all validators for all properties are
run if at least one form input is bound to a model property. The
ValidateModel is like the method TryValidateModel except that the
TryValidateModel method does not throw an InvalidOperationException
exception if the model validation fails.
ValidateModel - throws exception if model is not valid.
TryValidateModel - returns bool value indicating if model is valid.
class ValueController : Controller
{
public IActionResult Post(MyModel model)
{
if (!TryValidateModel(model))
{
// Do something
}
return Ok();
}
}
Validate Models one-by-one
If you validate a list of models one by one, you would want to reset ModelState for each iteration by calling ModelState.Clear().
Link to the documentation
//
var context = new ValidationContext(model);
//If you want to remove some items before validating
//if (context.Items != null && context.Items.Any())
//{
// context.Items.Remove(context.Items.Where(x => x.Key.ToString() == "Longitude").FirstOrDefault());
// context.Items.Remove(context.Items.Where(x => x.Key.ToString() == "Latitude").FirstOrDefault());
//}
List<ValidationResult> validationResults = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(model, context, validationResults, true);
if (!isValid)
{
//List of errors
//validationResults.Select(r => r.ErrorMessage)
//return or do something
}
I found this to work and do precisely as expected.. showing the ValidationSummary for a freshly retrieved object on a GET action method... prior to any POST
Me.TryValidateModel(MyCompany.OrderModel)
Related
suppose I have the following class
class A
{
[Required]
public string Name {get; set;}
[Required]
public string NickName {get; set;}
[Required]
public string UserId {get; set;}
}
and from the form I am passing only the Name and NickName to controller and before checking the model state simply I assign the user id to the UserId property as below
[HttpPost]
public IActionResult Save(A model)
{
model.UserId = User.GetLoggedInUserId<string>();
if (!ModelState.IsValid)
{
return View(model);
}
}
even though I have assign the user id before checking the model state it still returns the validation state false and complaining the for the user id. one way to come out of this problem is to create a view model which makes things more complex, because of assigning the values from view model to class it self.
any idea how to solve this.
Note: the question is not only for the User Id in the class there maybe other properties as well that may not be passed from the form to controller and the values maybe assigned to them from controller
You could try to remove the 'UserId' from the model validation before calling ModelState.IsValid. Code like this:
[HttpPost]
public IActionResult CreateA(A a)
{
var state1 = ModelState.IsValid; // false
ModelState.Remove("UserId"); //using Remove method to remove the specified object from the model-state dictionary.
var state2 = ModelState.IsValid; // true
a.UserId = "SN1001";
if (ModelState.IsValid)
{
var data = a.UserId;
}
return RedirectToAction(nameof(Index));
}
The screenshot as below:
Besides, you could also try to use the TryValidateModel() method to validate the model again in the controller, code like this:
[HttpPost]
public IActionResult CreateA(A a)
{
var state1 = ModelState.IsValid; // false
ModelState.Remove("UserId");
a.UserId = "SN1001";
if (!TryValidateModel(a, nameof(a)))
{
var state2 = ModelState.IsValid;
}
if (ModelState.IsValid)
{
var data = a.UserId;
}
return RedirectToAction(nameof(Index));
}
The result like this:
Reference: Model State Rerun validation
Edit
[Note] If the ModelState.IsValid is false first, before rerun validation using the TryValidateModel method, we have to remove the error from the ModelState.
You can also pass the value of UserId field using a hidden field with a default value, like:
#Html.HiddenFor(m => m.UserId, new { #Value = User.Identity.Name });
This answer might help What does ModelState.IsValid do?
ModelState.IsValid indicates if it was possible to bind the incoming values from the request to the model correctly and whether any explicitly specified validation rules were broken during the model binding process.
There is no way to solve this. If you don't need this validation, then remove it altogether with the attributes and add the appropriate value handling logic with code.
The isValid will just validate the state on binding time and that's that.
I have a .NET Core project. I am using a view model to bind incoming data from request.
In view model I'm doing a data annotation [Required] validation. If the data is empty for that particular field, the execution returns from there without passing to a controller.
I need the execution to go to controller to modify the response with my custom messages along with model-state errors.
My model looks like
[Required(ErrorMessage = ErrorConstants.required)]
[StringLength(5, MinimumLength = 5, ErrorMessage = ErrorConstants.invalid)]
[RegularExpression("^[0-9]+$", ErrorMessage = ErrorConstants.incorrectInputFormat)]
public string ZipCode { get; set; }
My controller action method sample. Please guide.
if (viewModel == null)
{
_dictionary = _apiResponseService.SetResponse("false", ErrorConstants.nullOrIncorrect);
return Ok(_dictionary);
}
if(!ModelState.IsValid)
{
return Ok(_apiResponseService.SetResponse("false", ErrorConstants.invalidJson, ModelState.ToDictionary(kvp => kvp.Key.Replace("viewModel.", ""),
kvp => kvp.Value.Errors.Select(error =>
{
if (error.Exception == null)
{
return error.ErrorMessage;
}
else
{
return string.Format(ErrorConstants.required, kvp.Key.Split('.').Last());
}
}).FirstOrDefault())));
}
I have found out the workaround for this. In controller, comment out [ApiController] attribute and the work is done.
The new [ApiController] attribute has a feature where
validation errors automatically trigger an HTTP 400 response.
Most likely the model state is invalid. That is the reason why it is not hitting your action.
The default behavior is disabled when the
SuppressModelStateInvalidFilter property is set to true. Add the
following code in Startup.ConfigureServices after
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);:
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressConsumesConstraintForFormFileParameters = true;
options.SuppressInferBindingSourcesForParameters = true;
options.SuppressModelStateInvalidFilter = true; //<-- THIS HERE
});
So instead of removing the [ApiController] attribute altogether, keep it and disable that default behavior so that the controller can keep what ever advantages it provided and still get to check model state in the action.
Reference Build web APIs with ASP.NET Core
Good morning,
I would consider myself a little better than a beginner with ASP.NET, but I have a problem that I need help with that I feel should be fairly simple, but cannot get to work. I'm currently building a website and am looking to implement some validation rules when submitting a form. I know that I can write the validation directly for each page I'm creating within it's corresponding ActionResult method, but I would much rather write the validation code once as it's own method within the controller and call the method within each ActionResult (for example, my ActionResult Create, ActionResult Edit, ActionResult Review) in the controller. I want to do this to keep the code more simple and readable, especially because the validation code that I'm writing is about 500 lines long.
Here's just a portion of the code that I'm using:
if (ModelState.IsValidField("Name") && customerDatabase.Name== null)
{
ModelState.AddModelError("Name", "The customer's name is required.");
}
if (ModelState.IsValidField("AccountNumber") && customerDatabase.AccountNumber.Length != 10)
{
ModelState.AddModelError("AccountNumber", "The customer's account number must be 10 digits long.");
}
if (ModelState.IsValidField("Address") && customerDatabase.Address == null)
{
ModelState.AddModelError("Address", "The customer's address is required.");
}
Thank you in advance for your help!
Consider using validation attributes for the simple validations on the model
In MVC, validation happens on both the client and server.
Fortunately, .NET has abstracted validation into validation attributes. These attributes contain validation code, thereby reducing the amount of code you must write.
for example
public class CustomerDatabase {
[Required]
public string Name { get; set; }
[StringLength(10)]
public string AccountNumber { get; set; }
[Required]
public string Address { get; set; }
//...other properties
}
This will also allow client side validation in a MVC View
It will reduce the amount of repeated validation needed on the server
if(ModelState.IsValid) {
//...
}
return View(customerDatabase);
Another alternative could be to create a custom action filter to apply validation checks
public class ValidateCustomerModelAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext context) {
var ModelState = context.Controller.ViewData.ModelState;
var customerDatabase = (MyModel)ModelState.Value; //cast to expected model type
if (ModelState.IsValidField("Name") && customerDatabase.Name== null) {
ModelState.AddModelError("Name", "The customer's name is required.");
}
if (ModelState.IsValidField("AccountNumber") && customerDatabase.AccountNumber.Length != 10) {
ModelState.AddModelError("AccountNumber", "The customer's account number must be 10 digits long.");
}
if (ModelState.IsValidField("Address") && customerDatabase.Address == null) {
ModelState.AddModelError("Address", "The customer's address is required.");
}
//...
}
}
and use it on the desired actions
[ValidateCustomerModel]
[HttpPost]
public ActionResult Create(MyModel customerDatabase) {
if(ModelState.IsValid) {
//...
}
return View(customerDatabase);
}
Reference Model validation in ASP.NET Core MVC
After trying around the whole day with model bindings, without results, i decided to ask here.
I have got an asp.net razor view where a user (aka Seller) can edit his user details. Furthermore the user should be able to change his password.
I made a ViewModel:
public class EditSellerViewModel
{
public Seller Seller { get; set; }
public ChangePasswordModel ChangePasswordModel { get; set; }
}
My view has two forms which result in two "Submit" buttons. In my action i check which button was clicked. If the "Passwords" form has been submitted, i want to set the new Password in the Seller entity (that actually works) and SaveChanges() which does not change anything in the database (and does not throw any exception). It simply does nothing.
Furthermore if the "Seller Detail" form was submitted, i want to save the sellers data. But TryUpdateModel is always false, even if i use the second parameter which enables the prefix for ViewModels.
[HttpPost]
public ActionResult EditUser(string btnSubmit, FormCollection formValues, EditSellerViewModel editSellerViewModel)
{
int uid = baseFunc.GetIdForUsername(User.Identity.Name);
var seller = bmDBCont.SellerSet.Single(s => s.Id == uid);
if (btnSubmit == "saveSellerPassword")
{
seller.Password = editSellerViewModel.ChangePasswordModel.NewPassword;
bmDBCont.ObjectStateManager.ChangeObjectState(seller, System.Data.EntityState.Modified);
bmDBCont.SaveChanges(); //<-- does nothing
}
if (TryUpdateModel(seller, "Seller")) //<-- never true
{
bmDBCont.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.Titles = CommonListsProvider.GetTitles();
ViewBag.Countries = CommonListsProvider.GetCountries();
return View(editSellerViewModel);
}
Here some debug info screenshots:
formcollection with seller form submitted
formcollection with password form submitted
Please can anyone help me?
See the documentation about TryUpdateModel, its says "Updates the specified model instance using values from the controller's current value provider and a prefix."
The Prefix to use when looking up values in the value provider.
Try use TryUpdateModel(seller) simple method without the "prefix" parameter.
if(TryUpdateModel(seller))
http://msdn.microsoft.com/en-us/library/dd493137(v=vs.108).aspx
I've been searching around and I'm not able to find an answer on what seems like a simple requirement:
With MVC Data Annotation validation, can you show the validation message ('must be a string with a maximum length of 5') in the validation summary or next to field, but clear the value of the text box (when validation fails).
I've tried to use ModelState.Clear() and ModelState.Remove("CompanyName"), but this clears both the value and validation message (validation state).
I'm asking this because recently we had a penetration test and one of the recommendations was to not pre-populate secure values (credit card number etc) if validation fails. This is obviously a minor issue, but the recommendation was to not send the value back across the internet (from the server) if we didn't have to.
Here is the code I'm working with:
public ActionResult Edit()
{
return View();
}
[HttpPost]
public ActionResult Edit(CompanyInput input)
{
if (ModelState.IsValid)
{
return View("Success");
}
//ModelState.Clear // clears both the value and validation message
//ModelState.Remove("CompanyName") // same result
return View(new CompanyInput());
}
And the view model:
public class CompanyInput
{
[Required]
[StringLength(5)]
public string CompanyName { get; set; }
[DataType(DataType.EmailAddress)]
public string EmailAddress { get; set; }
}
And the view:
#model Test.Models.CompanyInput
<h2>Edit</h2>
#using (Html.BeginForm("Edit", "Company"))
{
#Html.EditorForModel()
<button type="submit">Submit</button>
}
The ModelState of each field holds more than just the value, so removing it from the collection outright removed your error message as expected. I believe you should be able to clear just the value however, by doing something like.
ModelState["CompanyName"].Value = null;
EDIT: Upon closer inspection I found that the Value property is of type ValueProviderResult, simply nulling it doesn't give the desired result, and because the properties of this class appear to be getters only you have to replace the instance with your own. I've tested the following and it works for me.
ModelState["CompanyName"].Value = new ValueProviderResult(string.Empty, string.Empty, ModelState["CompanyName"].Value.Culture);
Because the ModelState isn't valid, you will either have to create a custom validator or a jQuery ajax/json call to determine if the data needs to be cleared or not.
Just changing the model property to string.Empty or something like that won't do the trick because the entire view gets re-rendered with the previous successful posted model but with the ModelState validation errors.
Yes you can add error message like this
[Required(ErrorMessage = "must be a string with a maximum length of 5")]
Update after clarity from OP:
To clear e.g. Input.Field = string.Empty;
You can create a custom validation class which is inherited from ValidationAttribute class
The following link gives a clear idea about how to implement custom validation class suitable for your problem.
Custom Data Annotation