ModelState.IsValid = False - c#

ModelState is throwing an error that one of the parameters [UserId] is null. That field isn't being set on my form. It's being set in the controller.
How do I remove it from the ModelState.IsValid test?
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,UserId,Created,TimeStamp,Name,Role,Description,Partner,PartnerAmount,Competitor,IsDeleted")] Relationship relationship)
{
relationship.UserId = User.Identity.Name.Replace(#"XXXXX\","");
relationship.Created = DateTime.Now;
relationship.TimeStamp = DateTime.Now;
relationship.IsDeleted = false;
if (ModelState.IsValid)
{
_context.Add(relationship);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(relationship);
}
It won't pass ModelState.IsValid b/c of UserId. But, it doesn't check Created, Timestamp or IsDeleted. Why doesn't it check those, but it does check UserId? All of these fields are required in my Model.

Remove the field from the view model. If you don't have one, create on. Using entities in your views generates security vurnabilities as an attacker could set fields that you might not change once posted.
Using an view model makes sure that only fields that are allowed to be changed are available in the browser.
If you still want to use your entity, change the UserId to int? which tells the model validator that it doesn't have to be set.
You can also remove the validations ModelState["UserId"].Errors.Clear(); But I strongly discourage from that. Use a specific view model.

You can't change ModelState.IsValid by changing any properties inside of the controller. IsValid still will be the same, since a list of errors will be the same. UserId should be assigned before the model reaches the controller.
So assign UserId=0 if it is nullable and add this line somewhere inside of the form of the view,
<input type="hidden" asp-for="#Model.UserId" value="#Model.UserId"/>

In the controller action method, try to use Rerun validation, call the TryValidateModel method, as shown here:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,UserId,Created,TimeStamp,Name,Role,Description,Partner,PartnerAmount,Competitor,IsDeleted")] Relationship relationship)
{
relationship.UserId = User.Identity.Name.Replace(#"XXXXX\","");
relationship.Created = DateTime.Now;
relationship.TimeStamp = DateTime.Now;
relationship.IsDeleted = false;
if (!TryValidateModel(relationship, nameof(relationship)))
{
return View(relationship);
}
_context.Add(relationship);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}

Related

satisfying the validation before checking the validation

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.

Does going from one Action method to the other action method clear class variables?

So in the same controller I have a Login Action method like this:
public ActionResult Login()
{
LoginModel model = this.LoginManager.LoadLoginPageData();
this.ForgotPasswordMethod = model.ForgotPasswordMethod;
return View(model);
}
Notice I set a variable there: ForgotPasswordMethod
So now when there on that page if they click on a link, it call another action result in the same controller class like this:
public ActionResult ForgotPassword()
{
if (!string.IsNullOrWhiteSpace(this.ForgotPasswordMethod) && this.ForgotPasswordMethod.Trim().ToUpper() == "TASKS")
return View();
return null; //todo change later.
}
Notice I tried to read the value of ForgotPasswordMethod , but it was NULL but it is NOT null when I am in the Login() method. So what should I do?
ASP.NET MVC was designed to return back to a cleaner, more straightforward web world built on HTTP, which is stateless, meaning that there is no "memory" of what has previously occurred unless you specifically use a technique that ensures otherwise.
As a result, whatever state you set via one ActionResult will no longer be the same state that exists when another ActionResult is invoked.
How do you "fix" this? You have a variety of options, depending on what your needs are:
Render the value to the client and post the value back to your second ActionResult method.
Store the value as a header and check that header.
Store the value in a cookie and check the cookie.
Store the value in session.
Store the value in a database.
Store the value in a static dictionary.
what if you store forgotpasswordmethod in Viewbag like
public ActionResult Login()
{
LoginModel model = this.LoginManager.LoadLoginPageData();
Viewbag.ForgotPasswordMethod = model.ForgotPasswordMethod;
return View(model);
}
then in the link of your page you can pass the value from the ViewBag
<a href=#Url.Action("ForgotPassword", "Name of your Controller", new { methodName = ViewBag.ForgotPasswordMethod })>Forgot Password</a>
Change your forgotpassword to
public ActionResult ForgotPassword(string methodName)
{
if (!string.IsNullOrWhiteSpace(methodName) && methodName.Trim().ToUpper() == "TASKS")
return View();
return null; //todo change later.
}

Handling Http Post and Get in the same view - ASP.Net MVC Razor

My view has 2 label 2 text-boxes and a button.
This is my controller :
public ActionResult Create()
{
// Custom model that holds values
// this is to set the default values to the text boxes
return View(model);
}
[HttpPost]
public ActionResult Create(CustomModel viewModel )
{
try
{
// TODO: Add insert logic here
// The button should trigger this method to perform update
return RedirectToAction("Create");
}
catch
{
return View();
}
}
When I run the program it automatically goes to my model which contains no value and throws a null pointer exception. Any ideas on how to preserve the model state. ?
Update :
I am using classic ADO.Net Model not the entity framework.
The http get and the post method follows a different logic. The Model with values are returned using the get method and the state of this model needs to be preserved for updating the corresponding records to the database using the post method.
But, when I use the post method, the compiler automatically routes to my model class looking for a parameter less constructor. I believe it is creating a new instance of my model class.
Not entirely sure I follow, but you can return the same view from your POST action, passing in the model. This will preserve the model data.
[HttpPost]
public ActionResult Create(CustomModel viewModel )
{
try
{
// TODO: Add insert logic here
// The button should trigger this method to perform update
// Return "Create" view, with the posted model
return View(model);
}
catch
{
// Do something useful to handle exceptions here.
// Maybe add meaningful message to ViewData to inform user insert has failed?
return View(model);
}
}
Well you can put validation in your view and then use ModelState.IsValid property as shown in below code :-
[HttpPost]
public ActionResult Create(CustomModel viewModel )
{
if (ModelState.IsValid)
{
////Insert logic here
return RedirectToAction("Create");
}
return View(viewModel);
}
Your "Get" method is returning a model that isn't present in your code(from what you have shown here). Below is a way to have your Action accept GET & POST.
[AcceptVerbs(HttpVerbs.Get|HttpVerbs.Post)]
public ActionResult Create(CustomModel viewModel)
{
try
{
// TODO: Add insert logic here
// The button should trigger this method to perform update
return RedirectToAction("Create");
}
catch
{
return View();
}
}
You have to create new instance in your Action Result method.
public ActionResult Create()
{
// Assuming - First Time this ActioResult will be called.
// After your other operations
CustomModel model = new CustomModel();
// Fill if any Data is needed
return View(model);
// OR - return new instance model here
return View(new CustomModel());
}

How can I validate with ModelState after adding values on the controller?

I have the following [post]create method on my controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include="Username","Name")] Admin admin)
{
// I assign the current date as the value to the HireDate property
admin.HireDate = DateTime.Today;
if (ModelState.IsValid)
{
// I do the insert
}
return View(admin);
}
ModelState.IsValid returns as false.
I looked at the ModelState object and found that the error IS in the HireDate property, because it's a non null field and the value is still null in the ModelState object.
I don't know much about ModelState but I'm assuming it's only validating the model built with the POST call.
Is there a way to "update" the ModelState object with the new data that I assigned on the controller (admin.HireDate = DateTime.Today)?
A more appropriate approach would be to assign the property before the Page even renders. So on your Create method that returns the original view you can do this.
public ActionResult Create()
{
Admin admin = new Admin();
admin.HireDate = DateTime.Today;
return View(admin);
}
You will then have to use #Html.HiddenFor(x => x.HireDate) in order for the View to be able to send it back to the Controller.

How does the ModelStateDictionary in ASP.NET know which model to validate?

Take this bit of generated code for example:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Bill bill)
{
if (ModelState.IsValid)
{
db.Entry(bill).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(bill);
}
There is a Model called "Bill", that has some [Required] attributes set for it.
My question is: how does the ModelState.IsValid know that it should be checking the Bill entity as opposed to another entity?
There's a default model binder in ASP.NET MVC called DefaultModelBinder. This class will automatically execute for each of the action parameters you defined. And when it attempts to instantiate and populate the corresponding model from the request key/value string pairs, it might encounter errors that this model binder simply adds to the ModelState dictionary. The reason why it might encounter errors is because you might have decorated your model with validation attributes.
So once the code execution enters the controller action, the ModelState.IsValid property will return false if there are errors added to it during model binding.
By the way your code is equivalent to the following (never to be used, just for illustration purposes):
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit()
{
Bill bill = new Bill();
if (TryUpdateModel(bill))
{
db.Entry(bill).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(bill);
}

Categories