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" />
<% } %>
Related
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.
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.
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);
}
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();
}
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.