ASP.NET MVC post to controller action from same controller - c#

I'm working on this project that currently has the follow method:
[HttpPost]
public ActionResult Service(string identifier)
This function is currently being used by a webpage form
<form method="POST" action="/Connector/Service">
<input type="hidden" id="identifier" name="identifier" value="#TempData["Identifier"]"/>
So Service is used when the user clicks on a button that submits the form on the webpage. The user is taken to this page from another action.
I have to implement a feature where sometimes instead of taking the user to the webpage, the user goes directly to the Service method from the action.
I found this thread when searching: ASP.NET MVC: RedirectToAction with parameters to POST Action
But it seems like that is bad design and returning RedirectToAction with Service actually did not work for me.
return RedirectToAction("Service", new {identifier})
Upon more search it seems like I actually cannot make a post request from my controller. Asp.NET MVC : redirect to another controller with POST Action
Any ideas on what I could do here? I am fairly new to ASP.NET and have no idea what to do at this point. All help is appreciated, thanks.

So turns out I could just call the function directly by doing
Service(identifier)
I kept thinking I had to use RedirectToAction so that didn't even cross my mind.
Thanks Sherlock for the answer!

The tag [post] is used only when a form is submited by the view itself, if your page redirect to a controler function from another view than the one your in, you have two option:
Use the [get] tag, you will probably have more luck with this tag, although i don't know exactly how it work (this might be edited)
Use the tagless function with parameters, if you rediret from another view to a function with a parameter, like public ActionResult Service(string identifier) that has no tag, you will automatically reach this function.
if the parameter haven't been specified, it will simply be NULL.

Related

Controller that calls a partial view that should display inside the Layout view

In a standard MVC application we have _Layout.cshtml and an Index.cshtml view. Now imagine if the user receives an email activation after a registration and now he/she clicks on that link.
The link points to /Account/ActivateAccount and after I process the activation, I wanted to redirect the user to the Login.cshtml partial view displaying a message.
I'm aware I can pass the message via the TempData["Message"] but what I don't know is how to redirect to a partial view and have that display inside the _Layout.cshtml instead that by itself.
At least this is what is happening when I call RedirectToAction("Login", "Home")
Note: all "home" based partial views are displaying within a < div id="divmain"> and my ideal solution would be to be able to decide what view should display inside that div, from the controller.
What's the right way to call a Partial View to be displayed from within another Controller ??
I'm not entirely sure I understand the question but I think what you're asking is if you can call a partial view from the _layout? This can be done with #Html.Action() helper. It will call out to a controller and method you specify and insert the result. The method you call would just be a PartialResult that returns the partial view with whatever data you need.
This is in contrast to #Html.Partial() which will render the partial view in place with whatever data you provide without routing back through a controller.
EDIT:
To summarize the comments to this answer, it seems I misunderstood the requirement. A user receives an email and clicks the link to activate their registration. The controller action that handles that request activates the user and then it needs to redirect to something. In this case, there was already a partial view which would serve the purpose but it couldn't be redirected to directly for some reasons. My suggestion was to just create a new View which contained a #Html.Partial() call to basically wrap the partial in a full view page.
The action method which handled the click could then just return that view directly or, if you consider the click of the link to be a "post" since it changes the application model by validating the user, you would create a new controller action and return RedirectToAction() directly. This would be like following the Post, Redirect, Get method and would prevent some issue if the user tried to refresh the "activated" page. It would also give more control over the URL naming.

Calling the HomePage and having it display a PartialView (ie.ResetPassword) from external link

Imagine the ForgotPassword sent an email with a link to Recover the password. Ideally we want the RecoverPassword to be a PartialView and it has to run inside the HomePage itself.
The external link passes a GUID.
QUESTIONS:
1) What's the right way to tell the Home Page to display the Partial View only on this specific case?
2) What would be the URL link look like?
3) What would the HomePage Index Controller look like so that it would also handle the possibility of a ResetPassword external link request?
You could do something simple like have a query string. for example www.yoururl.com/index?showresetpassword=true
Then inside your view you can add an if statement to render the partial view or not. If you need it inside of the controller I suggest you have the parameter as a nullable bool. Something like
public ActionResult Index(bool? showresetpassword)
Make it nullable so that if it is not inside of the url you will not have any isssues.

Is using tempdata validate method entry and pass info through multiple POSTs in the controller a bad practice? Alternatives?

First, some context:
In my controllers, I return RedirectToAction's on successful HTTP POSTs. I use TempData to hold onto the user's entered model data so that the method I redirect to can use this input data again.
Example:
1. Enter userID into search field.
2. Click button, POST is performed, user is found in database through my repository, userID stored in TempData, call RedirectToAction("Edit")
TempData["user"] = searchViewModel.userID;
return RedirectToAction("Edit");
perform edits on Edit view, click commit button, user info is stored in TempData, call RedirectToAction("Confirm");
display changes made on the Confirm view, click "Confirm", final HTTP POST is performed and changes are made through my repository service.
This seems to work well. In order to handle people trying to skip ahead to a page in the address bar by typing "../Edit/Confirm" I have this check in my Confirm method:
if (TempData["editUserViewModel"] == null)
return RedirectToActionPermanent("Edit");
Is this the best way to handle address bar input? I also do TempData.Keep("editUserViewModel") so that refreshes work. Is this the best way to handle refreshes?
For going from step 1 to 2, I would suggest a paramaterized action instead:
Enter userID into search field.
Click button, POST is performed, user is found in database through my repository
Call RedirectToAction("Edit", new {UserId = foundUserId})
Also, when searching, you probably shouldn't be doing a POST. A GET is just fine when you are looking for information and not mutating it. This avoids the PRG pattern altogether for the first place where you are using tempdata, since you do a GET instead of a POST.
As for the confirm, there is another way to do this without tempdata. Instead of redirecting to your Confirm action, POST to it, and return your confirm viewmodel. Only after that second POST do you hit the repos and finish out the PRG pattern after the POST with a Redirect and finally a Get.
Users should not be able to do any type of GET for your Confirm action, as can be seen by your bandaid for it. So, just don't allow gets at all. POST from the edit form to the confirm action, return a view, and then POST from that view to a second POST action method. Since these are all part of the same process, you shouldn't have to deal with redirects or tempdata until the process is complete (repos updated).
Update (reply to comments)
1.) If I remove the [HttpPost] attribute on my SearchUser function, how will my search button on the view know what to call?
Your search button is nested within a <form> HTML element. You will need to change the form's method to GET. If the attribute is not present, I believe POST is the default. Your search button will remain the same, but the form will submit the user-enetered input as an HTTP GET request instead of an HTTP POST:
<form method="GET">
...
<input type="submit" value="Search" />
</form>
2.) Can you clarify removing the Redirect to Confirm? I'm having trouble understanding how I would change a Redirect to a POST
It's difficult to explain this to someone just starting with web development, but in essence, every redirect is always an HTTP GET request. This is why you had to put the data into session (tempdata uses session) in order to maintain it across stateless requests.
Basically, here is your workflow:
User enters search input and clicks submit
The search in (1) is sent as a GET request to some action method, which returns a view.
The view returned in (2) contains a <form method="POST" action="/Users/StillNeedsConfirmationAction"> with additional input elements. This is the form that will be used to edit data.
User inputs data in the form view from (3) and clicks submit.
The action method StillNeedsConfirmationAction on your UsersController accepts an HTTP POST with a viewmodel object. Rather than redirecting though, the action simply returns another view, passing the same viewmodel object.
The view returned in (5) contains a <form method="POST" action="/Users/ConfirmAndUpdateAction">. It will render hidden inputs for each text box or other form element in your previous POST form.
User clicks submit on the second form to confirm fields
The action method ConfirmAndUpdateAction on your UsersController accepts an HTTP POST with the same viewmodel object that your other POST action did. However instead of returning another view, this time it saves the data in your repository and redirects.

See which view contains the form that posted to a controller action

I have a contoller action that a number of forms will post to, all in different views.
Is there a way, in my controller action, to see which view contained the form that posted to it?
I need this to determine to where to redirect the action when the code in the post action is complete.
Thank you!
Two options...you can add a field to your form (or query string) that provides the redirect url. Or you can look at the HttpRequest.UrlReferrer field. It will provide you with the full URL which you have to parse to get the original form.
Hope this helps

mvc.net multiple forms childaction

I have 2 forms on a page, they are included in the masterpage like so:
Html.RenderAction("Form1", "Controller")
and
Html.RenderAction("Form2", "Controller")
The Controller has the following:
<ChildActionOnly()>
Function Form1() As ActionResult
Return View("Form1", New ModelObject())
End Function
<ChildActionOnly()> <AcceptVerbs(HttpVerbs.Post)>
Function Form1(ByVal formCollection As FormCollection) As ActionResult
Return View("Form1", New ModelObject())
End Function
<ChildActionOnly()>
Function Form2() As ActionResult
Return View("Form2", New ModelObject())
End Function
<ChildActionOnly()> <AcceptVerbs(HttpVerbs.Post)>
Function Form2(ByVal formCollection As FormCollection) As ActionResult
Return View("Form2", New ModelObject())
End Function
The forms markup in the ascx is as follows, they are essentially the same form so the markup is very similar:
<% Using Html.BeginForm()%>
<%= Html.TextBoxFor(Function(model) model.Property1, New With {.class = "input"})%>
<input type="submit" class="submitbutton" value="" name="submit" />
<% End Using%>
The problem is, when I submit either form, it runs both post methods.
So Form1 post and Form2 post, yet the values in the form collection are from which ever form was submitted.
My question is:
Why is this submitting both forms with one set of form data?
How can I make it call only the relevant action with the correct form data?
I am sure I am making a simple mistake, just cannot see it for looking.
Project that demonstrates the problem can be found here: TestMVC.zip
Thanks in advance.
I have found a solution to the problem, was wondering if someone would like to comment on the correctness of this "work around".
ok... so step one, Remove the childonlyaction attribute from the post actions and add the controller/action to run when the form is submitted.
Html.BeginForm("Form1", "Form")
This makes sure that the correct post action is called.
The next step was to work out what I wanted to return.
So.. I need to return the custom model if there are validation errors etc. So thought I could do this using meta data or some other custom validation, add the model to TempData and then do a RedirectToAction making the action the page that I came from. i.e. /Home/Index or /Controller/Action
I get the controller/action from the referrer, that should always be set as this is coming from a post action.
Can anyone think of a better way of doing this?? As this is the only way I could find to give the results I want without using Ajax
I see that when you are rendering your forms, you are not naming them explicitly and you are also not mentioning form method. Can you please do something like this:
<% using (Html.BeginForm("ACTION", "CONTROLLER")) {%>
And if you are using child controls then why are you using "Html.RenderAction"? Shouldn't it be "Html.RenderPartial" like:
<% Html.RenderPartial("Search"); %>
If you take [ChildActionOnly] off of your Post actions, it only submits to one action at a time.
So one way to consider it would be to try and find out how to return a partial view as the whole page. Maybe by storing the page's route in a ViewModel around your model, and using RedirectToAction on that route?
This link (http://dotnetslackers.com/articles/aspnet/ASP-NET-MVC-2-0-Using-Multiple-Actions.aspx) seems to suggest that removing ChildActionOnly is all you need to do, but doesn't work in your sample. Most confusing.

Categories