【问题标题】:Post Redirect Get in ASP.NET MVC And Validation With Restful URLsPost Redirect Get in ASP.NET MVC and Validation with Restful URLs
【发布时间】:2011-07-29 02:57:13
【问题描述】:

我有一个用于编辑页面操作的宁静 URL。这在控制器上实现为一个 Edit 方法,它接受 GET 请求和一个 Edit 方法,它接受 POST 请求。

这意味着您可以访问编辑 URL,它将显示一个用于 GET 的表单或保存一个用于 POST 的表单。

[HttpGet]
public ActionResult Edit(int id) {
    ...
}

[HttpPost]
public ActionResult Edit(EditModel model) {
    ...
}

Post-Redirect-Get (PRG) 模式看起来很黑白,因为它本质上将每个 POST 重定向回 GET 操作。但是,我需要确信这是正确的做法。

我的计划是在 POST 操作中,如果模型有效,我将使用 Post-Redirect-Get 模式将用户发送到合理的位置(可能是 Index 或 Details 操作)。

但是,如果存在模型验证问题,我仍然想只显示视图。我不想重定向用户,因为这意味着将模型和 ModelState 填充到临时数据中并重定向到 GET 操作 - 然后将逻辑添加到 GET 操作中以处理临时数据。我可以通过简单地显示视图来避免所有这些。

是的,如果用户按下 F5,它将重新提交表单,并且他们将看到“重新提交警告”,但随后是同一页面(要求他们修复验证错误)。但是,他们似乎不太可能按 F5,而且也不存在重复提交的危险,因为表单将再次验证失败。

如果验证通过,用户将被重定向,并且不会重复提交。

那么我应该实现额外的代码并将数据填充到临时数据中以严格遵循 PRG 模式,还是在表单有效并且正在存储数据时使用 PRG 模式更明智?

【问题讨论】:

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


    【解决方案1】:

    只有在表单信息有效的情况下才应该进行重定向;在提交错误的情况下,从相同的 Edit 方法返回视图。

    这样做符合 PRG,因为如果您的模型无效,则不允许对服务器上的对象状态进行任何更改。 PRG 主要是为了防止多个帖子以不可预知的方式改变服务器对象(例如,业务对象、数据库表等)的状态;但是,在您的验证示例中,用户可以根据需要多次点击重新提交,并且它们将始终被发送回初始视图并显示验证错误——服务器上没有任何变化。因此,您做对了:仅当您的模型通过表示层中的验证时才发出重定向。

    【讨论】:

    • 这会破坏 PRG 并且做得很糟糕。如果您的服务器有请求配额,例如 5 分钟内不提交或重置,那么您在服务器上声明的没有更改是错误的。此外,页面刷新应该在执行您的建议时将页面设置为初始状态,您最终会重新显示错误和状态。假设仅在正文发生更改时才引入服务器更改,这不是设计运行在协议上运行的应用程序的好方法,该协议具有更多关于标题、动词等的信息。您必须始终假设 post 是如果您想实现良好的 HTTP 请求/响应流,就不要无能为力。
    【解决方案2】:

    尽管Ken's answer 确实强调了一个重要事实 - PRG 并不一定意味着“在发布时盲目地返回重定向” - 您有时仍可能想要进行重定向并保留模型状态。

    处理这种情况的最简单方法是使用操作过滤器将模型状态导出到会话(在重定向之前),然后导入模型状态(在执行新操作之前)。 Kazi Manzur Rashid 有几篇关于 ASP.NET MVC 最佳实践的优秀博文 (Part 1 Part 2)。它们已经很老了,但其中的许多技巧仍然非常适用。 first article 中的第 13 条提示正是您想要的。

    【讨论】:

    • 我使用 Kazi Manzur 解决方案的修改版本来验证失败,现在我非常喜欢“strict-PRG”。我知道这需要更多的努力,但是 strict-PRG 确实为用户提供了非常好的体验。
    • 我无法从任何有信誉的来源中找到任何支持 Kazi Manzur 的“解决方案”建议的方法的内容。他说在三篇链接的博客文章中提倡这种方法,一篇不再加载,另外两篇实际上提倡普通 PRG,你只在成功时重定向,并在失败时返回视图。这是一种非常糟糕的方法,因为它需要 state,而一个好的 HTTP 应用程序,也就是说 RESTful 应用程序不应该需要。
    • @ChrisPratt:我不知道您指的是哪些帖子没有加载 - 他在提示 #13 下的所有(四个)链接都为我加载。博客文章(以及答案和问题......)已经很老了,所以从那时起,被认为是“最佳实践”的内容可能已经发生了变化——今天,这很可能不是在无效后处理显示表单的最佳方式邮政。不过,我仍然认为这构成了宝贵的知识——以这种方式使用ActionFilter 是一种锤子,但并非所有东西都是匹配的钉子。与所有事物一样,在适当的情况下明智地使用它,而不是总是盲目地使用它。
    • @Endri:帖子来自 2011 年;如果当前版本框架中的最佳实践没有太大不同,我会感到非常惊讶。
    • 是的,我同意...但是当我不知道如何编辑它们时,这就是我从阅读如何处理损坏的链接中得到的。然而,它仍然是一个有价值的答案......可能更好地编辑以删除文章引用。
    【解决方案3】:

    PRG 是正确的做法。

    您对操作执行 POST,如果模型状态无效,您只需将模型状态数据“导出”到变量中并重定向到获取操作。

    与公认的答案相比,这样做的好处是您无需重写 [Post] 操作上的代码即可重新创建视图。

    在 get 操作中,您加载从帖子中导出的 ModelState。

    TempData 是做这些事情的好地方,代码是这样的:

    [HttpGet]
    public ActionResult Edit(int id) {
        // import model state from tempdata
        ...
    }
    
    [HttpPost]
    public ActionResult Edit(EditModel model) {
        // if modelstate is invalid
        // save modelstate in tempdata
        // redirect to Edit/{id}
        // else 
        ...
        RedirectToAction("List")
    }
    

    这可以使用 AttributeFilters 自动完成,@ben-foster 在这里创建了一个 excellent 帖子:

    Automatic Validation of Modelstate

    【讨论】:

    • 我认为你在这里创建了一个稻草人参数:This has the benefit as opposed to the accepted answer that you don't need to rewrite code on the [Post] action to recreate the view.你不需要根据接受的答案重写任何代码,你只需重新显示视图:return View(model);跨度>
    • 如果视图需要像去数据库获取一些值或做一些填充来创建视图模型,你应该通过 2 种方法(GET 和 POST)获取数据。
    • 还有其他方法可以避免代码重复 - 尽管在这种情况下模型被回发(注意签名),因此不需要数据库访问。
    • 我不知道如何避免代码重复,所以你可能是对的。关于模型发布,如果我清楚您所说的话,人们会认为发布的视图模型没有重新创建视图所需的所有数据。例如,发布的选择列表将仅包含所选列表的值,请注意实际选择列表。
    • Bart - 这可以通过让 ViewModel 包含可用的选择选项来工作;以及渲染它的视图。因此,当 POST 中的验证失败并且该控制器操作仅返回一个视图(模型)时,不需要任何工作来填充视图所需的所有内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-02
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 1970-01-01
    相关资源
    最近更新 更多