【问题标题】:PRG Pattern in ASP.Net MVC?ASP.Net MVC 中的 PRG 模式?
【发布时间】:2016-04-22 08:08:09
【问题描述】:

我是 ASP.Net MVC 的新手。在 PHP 中,即使发布请求无效,我也总是使用 PRG 模式。使用会话闪烁非常容易(也对用户友好)。

但是,在 ASP.Net MVC 中,当请求无效时,我看不到一种简单的 PRG 方法。我可以想到一些方法,但我认为它们不是好的做法,并且会增加一些不必要的工作。

此外,从我读过的几篇文章中,不鼓励在请求无效时使用 PRG。如果这是一种不好的做法,那么处理不成功的发布请求的更好方法是什么?没有PRG真的会更好吗?当用户尝试刷新页面时,我是否应该让相当烦人的浏览器警告?

【问题讨论】:

  • 正常的做法是返回view is ModelState is invalid 所以显示验证错误信息。
  • @StephenMuecke 我明白了。但是 POST 请求是否正常或可以接受?我的意思是,当您刷新所有浏览器警告时?
  • 是的,这有点痛苦。我已经看到一些尝试遵循 PRG 模式并仍然保持ModelState,例如在this blog 中(但不要使用TempData - 如果你刷新它丢失,除非你使用.keep().peek())跨度>
  • @StephenMuecke 我们使用的解决方案与博客中给出的解决方案非常相似(顺便说一句,我没有看到太多痛苦),但我看不出有什么替代方案TempData 可以使用(不会让事情变得非常痛苦):确实,在刷新时你会丢失无效的 ModelState,但这样的痛苦比没有 PRG 模式的刷新问题要少,你不觉得吗?
  • TempData 已失效,因为它仅对一个请求有效,并且在“选项卡”之间共享。这也使会话变得危险。无状态服务器是一个很好的目标。当您让客户重新发布数据时,您可以轻松实现。

标签: asp.net asp.net-mvc post-redirect-get


【解决方案1】:

在 Mvc 中,通常按如下方式处理您的 Post Action:

[HttpPost]
[ValidateAntiForgeryToken]
public virtual ActionResult LoginForm(LoginViewModel loginViewModel)
{
    if (!ModelState.IsValid) 
        return View("Login", loginViewModel);

    return Redirect("/");
}

如您所见,属性 ModelState.IsValid 会告诉您请求是否无效,因此当 Post 请求包含错误时,您可以返回相同的视图并在 ValidationSummary 中显示错误消息。这是视图的代码:

@using (Html.BeginForm("LoginForm", "Account"}))
    {
        @Html.ValidationSummary() // THIS WILL SHOW THE ERROR MESSAGES
        @Html.AntiForgeryToken()
        @Html.TextBoxFor(x => x.Email)
        @Html.PasswordFor(x => x.Password)
        <button type="submit">Submit</button>
    }

【讨论】:

  • 如果您在同一页面上有多个表单,这将如何工作?您需要将它们提交到不同的操作,否则 ASP.NET 无法区分提交的是哪个表单。
  • 当然,不同的表单发布到不同的action。每个动作都准备好接受特定于表单逻辑的强类型模型。
  • 但关键是,您在回答中说您必须返回到相同的 URL 以维护 ModelState。这意味着您不能发布到不同的操作,或者您丢失 ModelState - URL 更改,或者您必须进行重定向。
【解决方案2】:

我们已经在我们的 asp.net mvc web 应用程序中使用 PRG 模式大约 5 年了。我们采用 PRG 的主要原因是支持浏览器导航(例如后退、前进)。我们的网络应用程序供客户和前台/后台操作使用。我们典型的网页流程是从登录开始,然后通过许多列表/详细信息视图进行。我们还合并了部分视图,它们也有自己的视图模型。列表视图将具有用于导航的链接 (GETS)。详细视图将具有用于导航的表单 (POSTS)。

我们 PRG 的关键方面:

  1. 我们合并了视图模型,因此每个视图都有一个视图模型(所有数据访问都在视图模型中完成)。
  2. 每个视图模型都有一个 set() 和 get() 方法来维护与视图的最新实例关联的关键数据字段值。设置/获取值保存在会话状态中。
  3. set 方法对每个需要设置的值都有一个参数。 get 方法只是从 viewmodel 构造函数中调用来填充 viewmodel 的公共“键”值。
  4. viewmodel 还将有一个公共 load() 方法,用于获取其视图的所有必要数据。

我们的 PRG 模式概览:

  1. 在控制器中,每个操作都有一个单独的 GET 方法和一个 POST 方法。 GET 只显示一个视图; POST 处理发布的数据。
  2. 对于列表(菜单)视图,控制器 GET 方法调用目标视图的 set('item key values here') 方法,然后调用一个 RedirectToAction 到目标视图的控制器 GET 操作。
  3. 控制器 GET 方法将实例化视图模型(从而导致获取设置值),调用其加载方法,该方法使用设置/获取键值来获取数据,并返回视图/视图模型。
  4. 控制器 POST 方法要么让视图模型保存有效的发布数据,然后重定向到下一个所需页面(可能是上一个列表菜单) - 要么 - 如果数据无效,则重新显示当前视图。
  5. 我没有记录我们实现的所有 PRG 流程场景,但以上是基本流程。

示例视图模型设置/获取方法

private void GetKeys() {
  Hashtable viewModelKeys;
  if (SdsuSessionState.Exists("RosterDetail"))
  {
    viewModelKeys   = (Hashtable)SdsuSessionState.Get("RosterDetail");
    EventId         = (int)viewModelKeys["EventId"];
    SessionNo       = (int)viewModelKeys["SessionNo"];
    viewModelKeys   = null;
  }
}
public static void SetKeys(int eventId, int sessionNo) {
  Hashtable viewModelKeys = new Hashtable();
  viewModelKeys.Add("EventId",eventId);
  viewModelKeys.Add("SessionNo",sessionNo);
  SdsuSessionState.Set("RosterDetail",viewModelKeys);
  viewModelKeys = null;
}

样品控制器

[AcceptVerbs("Get")]

public ActionResult MenuLink(int eventId, int sessionNo, string submitButton) {
  if (submitButton == RosterMenu.Button.PrintPreview) {
    // P-R-G: set called viewmodel keys.
    RosterDetail.SetKeys(eventId,sessionNo);
    // Display page.
    return RedirectToAction("Detail","Roster");
  }
  if (submitButton == RosterMenu.Button.Export) { etc ...}
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-09
    • 1970-01-01
    • 2017-09-04
    • 2012-07-31
    相关资源
    最近更新 更多