Using modelstate.isvalid to validate data from inside the controller in MVC3 - asp.net-mvc-3

I am pretty new to ASP.NET MVC3 but i have about 4 years of experience with PHP frameworks.
I am trying to build an MVC3 web app, but i am having issues with validationg my model.
Here is a test controller to show you what i am trying without success to do.
I am trying to pass a value to my model inside the controller, but it doesnt take it into account the parameter.
I tried using modelstate.setmodelvalue, for junk.sentence, but it keeps the value from the POST request which is invalid an that i want to change by default (for test purposes) in the controller.
Can anyone help?
Thanks in advance.
Michael
[HttpPost]
public ActionResult Create(Junk junk)
{
//ModelState.Clear();
junk.sentence = "coucou";
ModelState.SetModelValue("sentence", new ValueProviderResult(junk.sentence, junk.number, null));
//ModelState
if (ModelState.IsValid)
{
db.Junks.Add(junk);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(junk);
}
//
// GET: /Junk/Edit/5
public ActionResult Edit(int id)
{
Junk junk = db.Junks.Find(id);
return View(junk);
}

Try removing it from modelstate:
[HttpPost]
public ActionResult Create(Junk junk)
{
junk.sentence = "coucou";
//ModelState
if (ModelState.IsValid)
{
db.Junks.Add(junk);
db.SaveChanges();
return RedirectToAction("Index");
}
ModelState.Remove("sentence");
return View(junk);
}
This assumes that in your view you have a corresponding input field that was generated using some of the Html helpers such as EditorFor for example:
#Html.EditorFor(x => x.sentence)
or:
#Html.TextBoxFor(x => x.sentence)

ModelState.IsValid returns false when there are model errors added to the model state. MVC validates the properties on your model and creates a list of errors for you in the ModelState. You must remove the errors from the model state that you wish to be ignored from inside your controller action. You can then update the actual values on the model. (Darin Dimitrov shows you an example of doing this)

Related

Retaining failed validation data

I've got a requirement to keep and display an incorrect entry on the view. It isn't possible to go to the next page without passing all validation.
For example, the use has to enter a date in text field in a certain format. Currently if the model binding fails it doesn't keep the invalid date text entry. I'd like to keep t and display it back to user with a vanadium failed message.
I'd like to know if this is achievable without creating a data holder type which temporarily hold the entered game and parsed value.
Yes, it is possible. Because you haven't posted any code, I'll just give you an example of how this can be achieved using server side validation.
Here is the [HttpGet] action method that serves up the form allowing the user to enter data.
[HttpGet]
public ActionResult Create()
{
// The CreateViewModel holds the properties and data annotations for the form
CreateViewModel model = new CreateViewModel();
return View(model);
}
Here is the [HttpPost] action method that receives and validates the form.
[HttpPost]
public ActionResult Create(CreateViewModel model)
{
if (!ModelState.IsValid)
{
return Create(); // This will return the form with the invalid data
}
// Data is valid, process the form and redirect to whichever action method you want
return RedirectToAction("Index");
}
You can also use return View(model); in the [HttpPost] action method instead of return Create();.

How to update the textbox value #Html.TextBoxFor(m => m.MvcGridModel.Rows[j].Id)

I have problem, that the text box value doesn't get updated with the new value in the model.
#Html.TextBoxFor(m => m.MvcGridModel.Rows[j].Id)
First the collection MvcGridModel.Rows get populated with some data, then when press button and submit the form it get new data successfully, but it doesn't update the textbox's value.
Do you have any ideas?
Thank u in advance
That's because HTML helpers such as TextBoxFor first look in the ModelState when binding their values and only after that in the model. So if in your POST action you attempt to modify some value that was part of the initial POST request you will have to remove it from the ModelState as well if you want those changes to take effect in the view.
For example:
[HttpPost]
public ActionResult Foo(MyViewModel model)
{
// we change the value that was initially posted
model.MvcGridModel.Rows[0].Id = 56;
// we must also remove it from the ModelState if
// we want this change to be reflected in the view
ModelState.Remove("MvcGridModel.Rows[0].Id");
return View(model);
}
This behavior is intentional and it is by design. This is what allows for example to have the following POST action:
[HttpPost]
public ActionResult Foo(MyViewModel model)
{
// Notice how we are not passing any model at all to the view
return View();
}
and yet inside the view you get the values that the user initially entered in the input fields.
There's also the ModelState.Clear(); method that you could use to remove all keys from the modelstate but be careful because this also removes any associated modelstate errors, so it is recommended to remove only values from the ModelState that you intend to modify inside your POST controller action.
All this being said, in a properly designed application you should not need this. Because you should use the PRG pattern:
[HttpPost]
public ActionResult Index(MyViewModel model)
{
if (!ModelState.IsValid)
{
// there was some error => redisplay the view without any modifications
// so that the user can fix his errors
return View(model);
}
// at this stage we know that the model is valid.
// We could now pass it to the DAL layer for processing.
...
// after the processing completes successfully we redirect to the GET action
// which in turn will fetch the modifications from the DAL layer and render
// the corresponding view with the updated values.
return RedirectToAction("Index");
}

Model doesn't get updated when back to Index

Models is populated within the ctor.
When I click Edit to edit an item ,it all works, and I can clearly see that the Model has been updated within Models after the TryUpdateModel() call.
However when its redirected to the index, Models doesn't have my changes any longer. What happened?
// GET: /Contact/
public ActionResult Index()
{
return View(Models);
}
// GET: /Contact/Edit/5
public ActionResult Edit(int id)
{
var contactModel = Models.Find((x) => x.ID == id);
return View(contactModel);
}
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var contactModel = Models.Find((x) => x.ID == id);
if (TryUpdateModel(contactModel))
{
return RedirectToAction("Index");
}
return View(contactModel);
}
I believe you only changed the model in memory from the Edit call, but not saved the changes down to persistant storage (database), so your ctor is reloading it from an unsaved state.
It depends on what you're using to persist your models (saving them to a DB). In my case I ran into the same problem and I had to make sure that I called something along an Update or Save method prior to my redirect since my model session was scoped to the HTTP request, a redirect didn't cause my session to be flushed, and changes saved.
In my case I was using nHibernate, and ended up adding an attribute to that when an action was finished, I flushed my changes.
The html helpers use ModelState by default on a post. It assumes if there hasnt been a redirect, there must have been an error so show the 'old' values.
You have to clear the modelstate (ModelState.Clear()) or come up with a different pattern : )

How do I perform error handling in ASP.NET MVC 3 controller?

I have an Account controller which have:
LoginForm (get action)
Login (post action)
RegisterForm (get action)
Register (post action)
In another controller's index action i use render them as:
Html.RenderAction("RegistrationForm", "Acount");
Html.RenderAction("LoginForm ", "Acount");
Everything works ok and I can register a new user, login and validate with unobtrusive validation.
The problem is when do some back-end validation in Register/Login action and if there is an error I don't know how to transfer the error to be rendered.
I've tried with PRG pattern and it works ok. I get the error displayed on the form with the preserved data, but PRG is not the way to do it.
What is an alternative solution to this problem without using ajax to validate or move those methods in the controller where the RegistrationForm/LoginForms are used?
I want to skip using TempData due to session usage in the background.
EDIT CODE SAMPLE:
class AccountController : SomeBaseController{
[HttpGet]
public PartialViewResult RegistrationForm()
{
return PartialView(new RegisterUser());
}
[HttpPost]
public ActionResult RegisterUser(RegisterUser user)
{
if (ModelState.IsValid)
{
var _user;// create domain user from Register user Model;
var _validationOutput = _userService.DoSomeAwsomeServerSideValidation(_user);// do some custom validation
if (_validationOutput.IsFault)
{
// we preseve tempdata in base controller OnActionExecuted
_validationOutput.ErrorMessages.ForEach(x => ModelState.AddModelError(_validationOutput.ErrorCode, _validationOutput));
// redirect to home controller custom error occured
return RedirectToAction("Index", "Home", user);
}
return RedirectToAction("RegistrationInfo");
}
return RedirectToAction("SomeUserInfoAction");
}
}
class HomeController : SomeBaseController {
Index(){
return View();
}}
HomeControllerMarkup {
#{Html.RenderAction("RegistrationForm", "Acount");}
#{Html.RenderAction("LoginForm", "Acount");}
}
You can manually add errors to your ModelState within your post controller using:
ModelState.AddModelError("", #"You didn't perform task XYZ");
You should then be able to return the view and display the errors in a validation summary:
#Html.ValidationSummary(false, "Login was unsuccessful because...")

Weird MVC Issue

I have this code and I can't understand why it works this way
I have a model and view which is arbitrary and a very simple (but weird) controller
Here is my controller:
public partial class RouteController : Controller
{
[HttpGet]
public virtual ActionResult Create()
{
Create create = new Create();
return View("Create", create);
}
[HttpPost]
public virtual ActionResult Create(Create route)
{
return Create();
}
}
The first create method loads the view as normal. When the view posts back it runs the 2nd action which runs the first (as expected). The wierd part is the view is (re-)loaded with my previously entered data with errors (if any). I dont understand this because my model is empty. I was expecting it to post back with the same form as if it was loaded for the first time but with errors possibly.
Please explain.
That's the normal behavior of HTML helpers and it is by design. They first look at values contained in the ModelState and after that in the actual model. If you intend to modify some values on the model in a POST action you need to remove them from modelstate first:
For example:
[HttpPost]
public virtual ActionResult Create(Create route)
{
ModelState.Remove("SomeProperty");
route.SomeProperty = "some new value";
return View(route);
}
If you intend to completely modify everything as in your example you could clear the modelstate entirely:
[HttpPost]
public virtual ActionResult Create(Create route)
{
ModelState.Clear();
return Create();
}
Another possibility is to write your own TextBoxFor, HiddenFor, CheckBoxFor, ... helpers that will use the value in the model and not the one in the model state. Or yet another (non-recommended) possibility:
<input type="text" name="SomeProperty" value="#Model.SomeProperty" />
Of course in this case client validation among other things provided by the standard helpers won't work.

Resources