The layout of this ASP.NET MVC application looks like the following (this is a mobile web app):
Page 1 is question 1 -> Submit the form and return Page 2's View
Page 2 is question 2 -> Submit the form and return Page 3's View
Page 3 is the final question -> Submit to complete processing
After each of the form submits, I'd expect the next page's view to appear and submit accordingly. But, what's really happening is on Page 1 when I hit the submit button, it shows Page 2's view, but now that I'm on Page 2 and I hit submit on that page, it calls the Action of Page 1's HttpPost.
What would cause Page 2's submit to call Page 1's HttpPost Action?
Here is the basic idea of what I'm doing:
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(MyModel myModel)
{
return View("Page2", myModel);
}
[HttpPost]
public ActionResult Page2(MyModel myModel)
{
return View("Page3", myModel);
}
[HttpPost]
public ActionResult Page3(MyModel myModel)
{
if (ModelState.IsValid)
{
// do something and complete
}
return RedirectToAction("Index", "Home");
}
This is obviously oversimplified, but you should get the idea. The Page2's View gets rendered no problem. But on that page when the submit button is hit, it goes right back to the HttpPost of Index().
Any ideas?
The reason why it happens is because when you use mvc's BeginForm HTML helper it submits to
the rendering action, not to the action that equals to the view name, but you can fix it!
Two options:
First: Set the values for the controller and action in the HTML helper:
#BeginForm("Page3", "controllerName")
Second: redirect to the action instead of returning the view:
[HttpPost]
public ActionResult Index(MyModel myModel)
{
return RedirectToAction("Page2", myModel);
}
[HttpPost]
public ActionResult Page2(MyModel myModel)
{
return RedirectToAction("Page3", myModel);
}
Related
In Microsoft ASP.NET MVC 5,
how do I sign an user out from an action other than the LogOff action of the Account controller? Some years ago I would use return RedirectToAction("LogOff","Account"), but nowadays it does not work anymore since LogOff is a Post action (not GET).
public ActionResult SomeActionOfSomeController() {
// some logic
return RedirectToAction("LogOut", "Account"); //does not work since LogOut has HttpPost attribute
}
This is the way I do it and it's using GET method, Is this what are you asking for ?
[HttpGet]
public ActionResult Logout()
{
WebSecurity.Logout();
// instead of displaying logout page directly we redirect to confirmation page.
// this will ensure auth cookie is cleared, which, in turn, ensures correct menu items are displayed
return RedirectToAction("LogoutConfirm");
}
[HttpGet]
public ActionResult LogoutConfirm()
{
return View();
}
Is your SomeActionOfSomeController present in Account Controller or some other other controller ?
If it is present in AccountController then you can do following thing instead of calling redicttoaction.
public ActionResult Index2()
{
return Index3();
}
[HttpPost]
public ActionResult Index3()
{
return Content("Test");
}
If it is from other controller action then you have to create object of AccountController and then call Method.
Hope this will help you.
You can do something like this right?
#using (Html.BeginForm("LogOff", "Account"))
{
#Html.AntiForgeryToken()
<button type="submit">Logout</button>
}
And you can do this, if you want a link, instead of button
#using (Html.BeginForm("LogOff", "Account",
FormMethod.Post, new { id = "LogOffForm" }))
{
#Html.AntiForgeryToken()
<a href="#Url.Action("LogOff", "Account")"
onclick="$('#LogOffForm').submit();">Logout</a>
}
Assuming you are implementing the default ASP.net login / logout, you can paste the following code in your Controller.
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return RedirectToAction("Index", "Home");
Note: These two lines are the same code that are present in Account/Logoff Controller Action method.
so I have a Url Action
Create new teacher & assign to account.
That passes in two routeValues: createAndAssign, and teacherID.
Now when I go to my Teacher/Create page, my URL is like so:
.../Teacher/Create?createAndAssign=True&teacherID=ea817321-5633-4fdc-b388-5dba2c4a728e
Which is good, I want this. Now when I POST to create my teacher, how do I grab createAndAssign and teacherID value?
You can set the Querystring value in a hidden variables in the form and render in your GET action method and accept that in your POST action method.
View rendered by your GET Action
#using (Html.BeginForm())
{
//Other form elements also
#Html.Hidden("teacher",#Request.QueryString["teacherID"] as string)
#Html.Hidden("createAndAssign",#Request.QueryString["createAndAssign"]
as string)
<input type="submit" />
}
and now have a teacher parameter and createAndAssign parameter in your HttpPost action method so that it will be available when you submit the form.
[HttpPost]
public ActionResult Create(string teacher,string createAndAssign)
{
//Save and Redirect
}
If your view is strongly typed (which is my personal preference), it is quite easy,
public ActionResult GET(string teacherID,string createdAndAssing)
{
var yourVMObject=new YourViewModel();
yourVMObject.TeacherID=teacherID;
yourVMObject.CreateAndAssign=createdAndAssing;
return View(createdAndAssing);
}
and in your strongly typed view,
#model YourViewModel
#using (Html.BeginForm())
{
//Other form elements also
#Html.HiddenFor(x=>x.TeacherID)
#Html.HiddenFor(x=>x.CreateAndAssign)
<input type="submit" />
}
And in your POST action
[HttpPost]
public ActionResult Create(YourViewModel model)
{
//look for model.TeacherID
//Save and Redirect
}
you can get the value from the query string or as params of the controller like
var x =Request.QueryString["createAndAssign"];
or
public ActionResult Create(bool createAndAssign, string teacherID){
return View();
}
A lot of my plain content is in the database, accessed by a custom CMS. Around the application I display simple "thank you" messages, etc. which consist of a controller action (simplified):
public ActionResult DetailsUpdated()
{
return View();
}
and my view:
#Html.GetContent("DetailsUpdated")
I have quite a few of these and its quite annoying having a lot of view files with one-liners in. I want to be able to return that content as a View, I can do return ContentResult(ContentRepository.GetContent("KEY")); but this returns as plain-text and there is no master view rendered.
So, basically, grab the content from the DB via ContentRepository.GetContent("KEY") (returns a string) and inject it into a master view, where RenderBody() is called. I'd like to have a custom ActionResult so I can just do:
public ActionResult DetailsUpdated()
{
return DbContentResult();
}
and then the DbContentResult ActionResult will find the content key relative to the action and controller name, go to the database and retrieve the content and display it within its master view, no physical file view needed. Is this possible?
You may have one view file and refer to that view file from several actions:
public class FooBarController : Controller {
public ViewResult Foo() {
return View("FooView", ContentRepository.GetContent("KEY"));
}
}
In this case, you will be able to render the view whose path is ~/Views/Shared/FooView.cshtml (unless you override the default convention of course).
Edit:
As you indicated, you can make a custom ViewResult which does this for you:
public class DbContentResult : ViewResult {
public DbContentResult() {
this.ViewName = "FooView";
this.ViewData.Model = "Foo Model";
}
}
Usage:
public ActionResult Index() {
return new DbContentResult();
}
Or even better, write an extension method for Controller class which integrates with DbContentResult:
public static class ControllerExtensions {
public static ViewResult DbContentResult(this Controller controller) {
return new DbContentResult();
}
}
Usage:
public ActionResult Index() {
return this.DbContentResult();
}
for more detail about creating custom actionresult go here:-
http://www.professionals-helpdesk.com/2012/06/create-custom-actionresult-in-mvc-3.html
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.
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" />
<% } %>