Have you ever been traveling through the "internets" and have been presented with the following?
As web developers we know what this means; a form was posted to the page and now you're trying to refresh that same page. I'm not sure if there's been any significant usability study on the subject but I would imagine my Grandmother wouldn't know what to do here. Enter the PRG pattern.
What is the PRG Pattern?
While the PRG pattern isn't knew, there isn't much out there on it for the .NET community. PRG stands for "Post/Redirect/Get", I'll let Wikipedia explain the rest:
instead of returning an HTML page directly, the POST operation returns a redirection command (using the HTTP 303 response code (sometimes 302) together with the HTTP "Location" response header), instructing the browser to load a different page using an HTTP GET request. The result page can then safely be bookmarked or reloaded without unexpected side effects.
While this could be accomplished in webforms, it would be much more difficult since the postback model hangs on pages posting to themselves to implement button clicks and the like. The MVC Framework on the other hand makes the implementation of the PRG pattern extremely easy.
But How? Can I See an Example?
I'm going to use an Login function as an example. If the login attempt is successful, the user should be redirected to their account page, otherwise they should be redirected back to the login page.
We first will need two actions, one for displaying the login view and one for processing the login attempt, which I've provided below:
1: /// <summary>
2: /// Displays the login view (the form to enter credentials)
3: /// </summary>
4: public ActionResult Login()
6: return View("Login");
9: /// <summary>
10: /// Handles form data from the login view, in other words, the form, which
11: /// is on "login.aspx" has a form tag with it's action set to "ProcessLogin",
12: /// the name of this method.
13: /// </summary>
14: public ActionResult ProcessLogin(string email, string password)
16: IUser user = userRepository.GetByEmail(email);
18: if (user != null && authenticator.VerifyAccount(user, password))
22: return RedirectToAction("Index", "Account");
25: //login failed
26: // add some message here in TempData to tell the user the login failed
27: return RedirectToAction("Login");
Notice the different return types in the both of the methods. Login() has one exit point, "return View("Login")" and ProcessLogin has two exit points both of which use RedirectToAction() call, which instead returns an objected of RedirectToRouteResult, a subclass of ViewResult. To properly perform PRG you must return a redirecting ViewResult from your action, such as RedirectToRouteResult, otherwise you'll get the dialog box pictured above.
Here is the login view (login.aspx). The important part to pay attention to is the "action" attribute.
1: <form name="loginActionForm" method="post" action="ProcessLogin" id="loginActionForm">
2: <fieldset class="login">
4: <label for="email">Email</label>
5: <input type="textbox" id="lemail" name="email" maxlength="100" value="" class="required" />
6: <label for="password">Password</label>
7: <input type="password" id="lpassword" name="password" maxlength="256" class="required" />
9: <input type="image" src="<%= AppHelper.ImageUrl("btn_go.gif") %>" class="submit" />
10: <div class="errorlist"></div>
By implementing the PRG pattern, I get nice clean urls that are bookmarkable. My users also get a nice site that is traversable and aren't presented with confusing security dialog messages