Altering the ASP.NET MVC 2 ActionResult on HTTP post - asp.net

I want to do some processing on a attribute before returning the view. If I set the appModel.Markup returned in the HttpPost ActionResult method below to "modified" it still says "original" on the form. Why cant I modify my attribute in a HttpGet ActionResult method?
[HttpGet]
public ActionResult Index()
{
return View(new MyModel
{
Markup = "original"
});
}
[HttpPost]
public ActionResult Index(MyModel appModel)
{
return View(new MyModel
{
Markup = "modified"
});
}

Because "original" is stored in ModelState. When form values are collected on MVC side, they are stored in ModelState object. You propably used Html.TextBox helper. When you recreate view after POST, it looks up into ModelState first and if there is posted value, it sets this value. Value in model object doesn't count anymore.
One of the solutions is to follow POST-REDIRECT-GET pattern. First POST, do something with data and then redirect:
[HttpPost]
public ActionResult Index(MyModel appModel)
{
//do something with data
return RedirectToAction("Index");
}
If you want to pass something between redirects, you can use TempData:
[HttpPost]
public ActionResult Index(MyModel appModel)
{
//do something with data
TempData["Something"] = "Hello";
return RedirectToAction("Index");
}
[HttpGet]
public ActionResult Index()
{
var something = TempData["Something"]; //after redirection it contains "Hello"
}
After redirect, ModelState is gone, so the is no value to override. POST-REDIRECT-GET pattern also helps to get rid of form reposting effect when you press F5 in browser.

Related

A multi-stage model replenishment

I have a need, to make a multi-step process in which my, let's say "big model" will be updated every step and in the end saved to a database. For now, I made it this way:
[HttpGet]
public ActionResult StepOne()
{
return View();
}
[HttpPost]
public ActionResult StepOne(StepOneViewModel viewModel)
{
return RedirectToAction("StepTwo", "SameController", viewModel);
}
[HttpGet]
public ActionResult StepTwo(StepOneViewModel stepOneViewModel)
{
var stepTwoViewModel = new StepTwoViewModel()
{
stepOneViewModel = stepOneViewModel
};
return View(stepTwoViewModel );
}
[HttpPost]
public ActionResult StepTwo(StepTwoViewModel viewModel)
{
return RedirectToAction("StepThree", "SameController", viewModel);
}
... And so on. This approach seems to be good because it's strongly typed in a view, and I can have a separate validation for every "smaller" view model. On the other hand, I have to pass every parameter of the previous view model via Hidden inputs (making a partial for example, not to mess the code too much).
But is there another, better way to do so ? Is it possible to pass the whole object with many properties to the hidden or something similar ?

asp.net mvc HttpPost call with RedirectToAction is not working. Passing in null model back to HttpGet method

I have a question about HttpGet and HttpPost methods in ASP.NET MVC.
I have two HttpGet method. The Product() is first called when entering the page for the first time.
I have a form to submit changes to the database. I want to call the Products(MyModel updatedmodel)
with RedirectToAction like RedirectToAction("Products", model) but it does not call that method. I
have a breakpoint in the overloaded HttpGet call but the overload method never gets called.
[HttpGet]
public ActionResult Products()
{
// first call
}
[HttpGet]
public ActionResult Products(MyModel updatedmodel)
{
}
The page loads with a form. I hit the select button and it calls the HttpPost method
[HttpPost]
public ActionResult SendChanges(MyModel model)
{
if (ModelState.IsValid)
{
// call database
MyModel updatedmodel = send
return RedirectToAction("Products", model);
}
return View("Products", model);
}
Next I changed to only 1 HttpGet call with the overloaded model parameter like so. The RedirectToAction does call
that 1 HttpGet method but the model is null with the RedirectToAction("Products", model) call.
Why is the model null if I call with RedirectToAction("Products", model) call? How can I fix it?
[HttpGet]
public ActionResult Products(MyModel updatedmodel)
{
}
[HttpPost]
public ActionResult SendChanges(MyModel model)
{
if (ModelState.IsValid)
{
// call database
MyModel updatedmodel = send
return RedirectToAction("Products", model);
}
return View("Products", model);
}
Because the overloaded version of the function you are using is
RedirectToAction(string actionName, object routeValues)
Watch the browser's network requests. RedirectToAction returns a 302 Redirect response, prompting the browser to your GET action with routeValues sent as query string parameters.
To pass a model directly to the redirected action you will need to use Session or TempData.
[HttpPost]
public ActionResult SendChanges(MyModel model)
{
...
TempData["productData"] = updatedModel;
return RedirectToAction("Product");
}
[HttpGet]
public ActionResult Product()
{
var model = TempData["productData"] as MyModel;
return View(model);
}
Or you pass an id through the query string and re-query the database.
[HttpPost]
public ActionResult SendChanges(MyModel model)
{
...
// call database
MyModel updatedmodel = send
return RedirectToAction("Products", new { id = updatedmodel.Id );
}
[HttpGet]
public ActionResult Product(int id)
{
var model = db.GetProduct(id);
return View(model);
}
The second method is an extra database query but less code. Also, TempData is only available for the next request and reloading Product action will no longer have the "productData" instance.

Difference Between ViewResult() and ActionResult()

What is the difference between ViewResult() and ActionResult() in ASP.NET MVC?
public ViewResult Index()
{
return View();
}
public ActionResult Index()
{
return View();
}
ActionResult is an abstract class that can have several subtypes.
ActionResult Subtypes
ViewResult - Renders a specifed view to the response stream
PartialViewResult - Renders a specifed partial view to the response stream
EmptyResult - An empty response is returned
RedirectResult - Performs an HTTP redirection to a specifed URL
RedirectToRouteResult - Performs an HTTP redirection to a URL that is determined by the
routing engine, based on given route data
JsonResult - Serializes a given ViewData object to JSON format
JavaScriptResult - Returns a piece of JavaScript code that can be executed on the client
ContentResult - Writes content to the response stream without requiring a view
FileContentResult - Returns a file to the client
FileStreamResult - Returns a file to the client, which is provided by a Stream
FilePathResult - Returns a file to the client
Resources
What's the difference between ActionResult and ViewResult for action method? [ASP.NET Forums]
ActionResult is an abstract class.
ViewResult derives from ActionResult. Other derived classes include JsonResult and PartialViewResult.
You declare it this way so you can take advantage of polymorphism and return different types in the same method.
e.g:
public ActionResult Foo()
{
if (someCondition)
return View(); // returns ViewResult
else
return Json(); // returns JsonResult
}
It's for the same reason you don't write every method of every class to return "object". You should be as specific as you can. This is especially valuable if you're planning to write unit tests. No more testing return types and/or casting the result.
ViewResult is a subclass of ActionResult. The View method returns a ViewResult. So really these two code snippets do the exact same thing. The only difference is that with the ActionResult one, your controller isn't promising to return a view - you could change the method body to conditionally return a RedirectResult or something else without changing the method definition.
While other answers have noted the differences correctly, note that if you are in fact returning a ViewResult only it is better to return the more specific type rather than the base ActionResult type. An obvious exception to this principle is when your method returns multiple types deriving from ActionResult.
For a full discussion of the reasons behind this principle please see the related discussion here: Must ASP.NET MVC Controller Methods Return ActionResult?
In the Controller , one could use the below syntax
public ViewResult EditEmployee() {
return View();
}
public ActionResult EditEmployee() {
return View();
}
In the above example , only the return type varies . one returns ViewResult whereas the other one returns ActionResult.
ActionResult is an abstract class . It can accept:
ViewResult , PartialViewResult, EmptyResult , RedirectResult , RedirectToRouteResult , JsonResult , JavaScriptResult , ContentResult, FileContentResult , FileStreamResult , FilePathResult etc.
The ViewResult is a subclass of ActionResult.
In Controller i have specified the below code with ActionResult which is a base class that can have 11 subtypes in MVC like:
ViewResult, PartialViewResult, EmptyResult, RedirectResult,
RedirectToRouteResult, JsonResult, JavaScriptResult, ContentResult,
FileContentResult, FileStreamResult, FilePathResult.
public ActionResult Index()
{
if (HttpContext.Session["LoggedInUser"] == null)
{
return RedirectToAction("Login", "Home");
}
else
{
return View(); // returns ViewResult
}
}
//More Examples
[HttpPost]
public ActionResult Index(string Name)
{
ViewBag.Message = "Hello";
return Redirect("Account/Login"); //returns RedirectResult
}
[HttpPost]
public ActionResult Index(string Name)
{
return RedirectToRoute("RouteName"); // returns RedirectToRouteResult
}
Likewise we can return all these 11 subtypes by using ActionResult() without specifying every subtype method explicitly.
ActionResult is the best thing if you are returning different types of views.
To save you some time here is the answer from a link in a previous answer at https://forums.asp.net/t/1448398.aspx
ActionResult is an abstract class, and it's base class for ViewResult class.
In MVC framework, it uses ActionResult class to reference the object your action method returns. And invokes ExecuteResult method on it.
And ViewResult is an implementation for this abstract class. It will try to find a view page (usually aspx page) in some predefined paths(/views/controllername/, /views/shared/, etc) by the given view name.
It's usually a good practice to have your method return a more specific class. So if you are sure that your action method will return some view page, you can use ViewResult. But if your action method may have different behavior, like either render a view or perform a redirection. You can use the more general base class ActionResult as the return type.

Ambigous Routes Using ASP.Net MVC

So far I still have the standard routing. What I tried to do is
public Foo : Controller
{
public ActionResult Index(int id)
{
return View("List", repo.GetForId(id));
}
public ActionResult Index()
{
return View("List", repo.GetAll());
}
}
The URL I entered was
localhost/Foo/Index.
I was under the asumption that it was smart enough to figure out which method I wanted. All I get though is an error telling me that the call is ambigous. From reading the route tutorial I thought that this would work. What am I missing?
Sorry:
Duplicate. I am voting to close.
Method overloading (two actions with the same name) resolution is based on the HTTP verb. This means that if you want to have two actions with the same name you need to differentiate them by the HTTP verb they accept:
public ActionResult Index(int id)
{
return View("List", repo.GetForId(id));
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index()
{
return View("List", repo.GetAll());
}
Thus when you call GET /Home/Index the first action will be invoked and when you call POST /Home/Index the second one will be invoked.
Also note that you should probably use nullable integer as the id argument because if no parameter is passed in the request, the model binder will fail.
A RESTful way to handle this, following conventions that I would suggest you is this:
public ActionResult Index()
{
return View(repo.GetAll());
}
public ActionResult Show(int id)
{
return View(repo.GetForId(id));
}
which will allow you to handle both /Home/Index and /Home/Show/10 requests. You probably will have different views as well because the first one will be strongly typed to IEnumerable<T> while the second one to T.

asp.net mvc set action explicitly

I have 2 views for a input operation in my application.
The first view (lets call it view1) submits a form. Based on the form some operations on database is done and second view(View2) is returned with some other data from the database as a model.
controller action code :
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult View1(FormCollection fc)
{
//database ops to fill vm properties
View2ViewModel vm=new View2ViewModel();
return View("View2", vm);
}
Now, since I return a new view and not a action redirect the url is still http://www.url.com/View1 but everything works as it is supposed to.
The problem:
When I submit the form in View2 it calls the View1 action method, not the View2 action method. Probably because the url is still View1.
What can I do to call the action View2
Your controller methods should look something like this:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult View1(int id)
{
//database ops to fill vm properties
View1ViewModel vm=new View1ViewModel(id);
return View("View1", vm);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult View1(FormCollection fc)
{
// Code here to do something with FormCollection, and get id for
// View2 database lookup
return RedirectToAction("View2", id);
}
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult View2(int id)
{
// Do something with FormCollection
return View("View2", vm);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult View2(int id)
{
// Do something with FormCollection
return RedirectToAction("View3", id);
}
...and so on.
You can be in any View you want and submit the form to any controller's action from your application, cuz you can specify that
<% using (Html.BeginForm("ThAction", "Controller"))
{ %>
Enter your name: <%= Html.TextBox("name") %>
<input type="submit" value="Submit" />
<% } %>

Resources