2
votes

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");
  1. perform edits on Edit view, click commit button, user info is stored in TempData, call RedirectToAction("Confirm");
  2. 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?

1

1 Answers

2
votes

For going from step 1 to 2, I would suggest a paramaterized action instead:

  1. Enter userID into search field.
  2. Click button, POST is performed, user is found in database through my repository
  3. 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:

  1. User enters search input and clicks submit
  2. The search in (1) is sent as a GET request to some action method, which returns a view.
  3. 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.
  4. User inputs data in the form view from (3) and clicks submit.
  5. 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.
  6. 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.
  7. User clicks submit on the second form to confirm fields
  8. 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.