This is a controller action that I call with ajax post method:
[HttpPost]
public ActionResult Add(Comment comment)
{
if (User.Identity.IsAuthenticated)
{
comment.Username = User.Identity.Name;
comment.Email = Membership.GetUser().Email;
}
if (ModelState.IsValid)
{
this.db.Add(comment);
return PartialView("Comment", comment);
}
else
{
//...
}
}
If the user is logged in, submit form doesn't have Username and Email fields, so they don't get passed by ajax call. When action gets called ModelStat.IsValid returns false, because these two properties are required. After I set valid values to properties, how do I trigger model validation to update ModelState?
You can use a custom model binder to bind the comment's Username and Email properties from User.Identity. Because binding occurs before validation the ModelState will be valid then.
Another option is to implement a custom model validator for the Comment class, that checks the ControllerContext.Controller for a validated user.
By implementing any of these options you can remove the first if check.
You can try calling the built in TryUpdateModel method which returns a boolean so you can check that value.
UPDATE: Try using TryUpdateModel with exceptions. Use a formcollection instead of Comment into the Action.
[HttpPost]
public ActionResult Add(FormCollection collection)
{
string[] excludedProperties = new string[] { "Username". "Email" };
var comment = new Comment();
if (User.Identity.IsAuthenticated)
{
comment.Username = User.Identity.Name;
comment.Email = Membership.GetUser().Email;
}
TryUpdateModel<Comment>(comment, "", null, excludedProperties, collection.ToValueProvider());
if (ModelState.IsValid)
{
this.db.Add(comment);
return PartialView("Comment", comment);
}
else
{
//...
}
}
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.
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.
}
I am trying to "re-run" validation of ModelState after I change model within my controller. Is this possible to do? Here is code example that explains it:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ContactUs(ContactUsModel model)
{
model.Email = "defaultemail#world.com";
// Email is required field and even though it's
// now valid ModelState.IsValid is false
// how to re-run validation?
if (ModelState.IsValid)
{
// want to get here
}
}
Clear ModelState and call TryValidateModel
In Your Case
public ActionResult ContactUs(ContactUsModel model)
{
model.Email = "defaultemail#world.com";
ModelState.Clear();
TryValidateModel(model);
if (ModelState.IsValid)
{
//Do something
You can call TryUpdateModel
public ActionResult ContactUs(ContactUsModel model)
{
model.Email = "defaultemail#world.com";
if (TryUpdateModel(model))
{
.... // model has been updated
Refer documentation.
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.
I have some code that saves a ticket in our system. If there is an error it does a RedirectToAction(). The problem is that I don't seem to have my errors in the new action. How can I fix this?
ModelState.AddModelError("_FORM", "Unable to save ticket");
ModelState.AddModelError("_FORM", "Phone number was invalid.");
ModelState.AddModelError("_FORM", "Lane number is required.");
return RedirectToAction("CreateStep", "Ticket");
I know some have suggested using TempData, but how would I get each error out of the ModelState?
Thanks.
The PRG pattern is ok, but I did this:
Base controller:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (TempData["ModelState"] != null && !ModelState.Equals(TempData["ModelState"]))
ModelState.Merge((ModelStateDictionary)TempData["ModelState"]);
base.OnActionExecuted(filterContext);
}
Action (I'm using xVal):
try
{
user.Login();
AuthenticationManager.SignIn(user);
}
catch (RulesException rex)
{
// on bad login
rex.AddModelStateErrors(ModelState, "user");
TempData["ModelState"] = ModelState;
return Redirect(Request.UrlReferrer.ToString());
}
The action throws an exception, adds the ModelState to TempData and redirects back to the referrer. Since the action is caught, OnActionExecuted is still executed, but the first time around the ModelState is the same as TempData["ModelState"], so you don't want to merge with yourself. When the redirect action is executed, OnActionExecuted fires again. This time, if there's anything in TempData["ModelState"], it merges with this action's ModelState.
You could expand it to multiple models by using TempData["ModelState.user"] = ModelState and then merging every TempData object that starts with ModelState..
I know this thread is old, but this blog about ASP.NET Best Practices has some excellent suggestions.
#13 on the page deals with using 2 Action filters to save and restore ModelState between redirects.
This is the pattern that my work uses, and I love it.
Here's the simplified example:
[ImportModelStateFromTempData]
public ActionResult Dashboard()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post), ExportModelStateToTempData]
public ActionResult Dashboard(string data)
{
if (ValidateData(data))
{
try
{
_service.Submit(data);
}
catch (Exception e)
{
ModelState.AddModelError(ModelStateException, e);
}
}
return RedirectToAction("Dashboard");
}
this blog post describes how you could implement the PRG-Pattern in MVC
http://blog.simonlovely.com/archive/2008/11/26/post-redirect-get-pattern-in-mvc.aspx
hth
Use the TempData[] Collection
The tempdata is stored from one request to the next, then its gone.
What I did to maintain my ModelState no matter where I go with redirects is the following:
In your model, add:
public ModelStateDictionary modelstate { get; set; }
In your model's constructor, add:
this.modelstate = new System.Web.Mvc.ModelStateDictionary();
Sample Post with my model called Models.ContactInformation:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult contact(Models.ContactInformation con)
{
if (string.IsNullOrEmpty(con.SelectedAgencySelectorType))
{
ModelState.AddModelError("", "You did not select an agency type.");
}
con.modelstate = ModelState;
TempData["contact"] = con;
if (!ModelState.IsValid) return RedirectToAction("contactinformation", "reports");
//do stuff
return RedirectToAction("contactinformation", "reports");
}
So now your tempdata has your model and modelstate as is.
The following is my view that is agnostic to the state of anything, unless it has something. Here's the code:
[HttpGet]
public ActionResult contactinformation()
{
//try cast to model
var m = new Models.ContactInformation();
if (TempData["contact"] is Models.ContactInformation) m = (Models.ContactInformation)TempData["contact"];
//restore modelstate if needed
if (!m.modelstate.IsValid)
{
foreach (ModelState item in m.modelstate.Values)
{
foreach (ModelError err in item.Errors)
{
ModelState.AddModelError("", err.ErrorMessage.ToString());
}
}
}
return View(m);
}