[翻译] ASP.NET MVC中的PRG模式

Translator: Anders Liu

Date: 2008-09-08

摘要: POST操作不是直接返回一个HTML页面,而是返回一个重定向命令(使用HTTP 303响应码(有时是302)以及HTTP的“Location”响应头),引导浏览器使用HTTP GET请求加载另一个页面。这个结果页可以安全地作为书签进行保存或重新加载,而不会带来非预期的副作用。

Have you ever been traveling through the "internets" and have been presented with the following?

当你在 internet上冲浪时,你是否见到过下面这玩意?

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.

作为 Web开发者,我们知道它的意义——表单已经POST到页面,但正在尝试刷新同一个页面。我不知道研究这个主题是否有什么重大意义,但我可以想象得到,我的奶奶遇到这个画面时肯定不知道该怎么办。使用PRG模式吧。

What is the PRG Pattern?
PRG模式是什么?

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:

尽管 PRG模式不是什么新鲜玩意,但在.NET社区强调的并不是很多。PRG表示“Post/Redirect/Get”,剩下的让Wikipedia来解释吧:

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.

POST操作不是直接返回一个HTML页面,而是返回一个重定向命令(使用HTTP 303响应码(有时是302)以及HTTP的“Location”响应头),引导浏览器使用HTTP GET请求加载另一个页面。这个结果页可以安全地作为书签进行保存或重新加载,而不会带来非预期的副作用。

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.

尽管 WebForms也能完成该功能,但非常复杂,因为页面的postback模型需要靠回发自身来实现按钮的单击等操作。而MVC Framework使得实现PRG模式变得非常简单。

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.

我将用一个 Login功能作例子。如果登录成功,用户会被重定向到他的帐户页面,否则会被重定向回登录页。

We first will need two actions, one for displaying the login view and one for processing the login attempt, which I've provided below:

我们首先需要两个操作,一个用于显示登录视图,另一个用于处理登录操作,如下所示:

view plaincopy to clipboardprint?
/// <summary>
/// Displays the login view (the form to enter credentials)
///
/// 显示登录视图(用于输入凭证的表单)
/// </summary>
public ActionResult Login()
{
    return View("Login");
}
 
/// <summary>
/// Handles form data from the login view, in other words, the form, which
/// is on "login.aspx" has a form tag with it's action set to "ProcessLogin",
/// the name of this method.
///
/// 处理来自登录视图的表单数据,换句话说,“login.aspx”中的表单的form标签
/// 的action被设置为“ProcessLogin”——该方法的名字。
/// </summary>
public ActionResult ProcessLogin(string email, string password)
{
    IUser user = userRepository.GetByEmail(email);
 
    if (user != null && authenticator.VerifyAccount(user, password))
    {
        authenticator.SignIn(user);
 
        return RedirectToAction("Index", "Account");
    }   
    
    //login failed
    // add some message here in TempData to tell the user the login failed
    
    // 登录失败
    // 在这里向TempData中添加一些消息,告诉用户登录失败了
    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.

注意两个方法的返回值类型的不同。 Login()只有一个出口“return View("Login")”,而ProcessLogin有两个出口,这两个出口都使用了RedirectToAction()调用,它返回的是RedirectToRouteResult类型——ViewResult的一个子类——的对象。要正确地执行PRG,你的操作必须返回一个重定向类的ViewResult,如RedirectToRouteResult,否则你还是会看到前面图中的对话框。

Here is the login view (login.aspx). The important part to pay attention to is the "action" attribute.

下面是登录视图( login.aspx)。着重注意一下“action”属性。

view plaincopy to clipboardprint?
<form name="loginActionForm" method="post" action="ProcessLogin" id="loginActionForm">
   <fieldset class="login">
       <legend>Login</legend>
       <label for="email">Email</label>
       <input type="textbox" id="lemail" name="email" maxlength="100" value="" class="required" />
       <label for="password">Password</label>
       <input type="password" id="lpassword" name="password" maxlength="256" class="required" />
       
       <input type="image" src="<%= AppHelper.ImageUrl("btn_go.gif") % />" class="submit" />
       <div class="errorlist"></div>
   </fieldset>
</form>

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.

实现了PRG模式之后,我的URL干净了,也能加书签了。我的用户也可以冲浪冲得更爽了,那些混乱的安全对话框消息再也不见了。您瞧准呵,PRG模式,还真对得起咱这张网页。

 

 

下一篇:关于使用GUID和Identity做主键的一些思考-刘彦博