【问题标题】:how can i keep my url when my validation fail in asp.net mvc controller action当我的验证在 asp.net mvc 控制器操作中失败时,我如何保留我的 url
【发布时间】:2010-07-20 10:49:07
【问题描述】:

如果我从详细信息页面开始:

http:\\www.mysite.com\App\Detail

我有一个名为 Update 的控制器操作,它通常会将 redirectToAction 调用回 detail 页面。但我在验证中发现了一个错误,我需要在重定向之前返回(以避免丢失我的所有 ModelState)。这是我的控制器代码:

 public override ActionResult Update(Application entity)
    {
        base.Update(entity);
        if (!ModelState.IsValid)
        {
            return View("Detail", GetAppViewModel(entity.Id));
        }
      return RedirectToAction("Detail", new { id = entity.Id }) 

但现在我看到带有验证错误消息的视图(因为我正在使用 HTML.ValidationSummary() ),但 url 看起来像这样:

http:\\www.mysite.com\App\Update

无论如何我可以避免 URL 改变而无需将 modelstate 放入一些临时变量中?这里是否有最佳实践,因为我见过的唯一示例是在调用 redirectToAction 之间将 ModelState 放在一些 tempdata 中。

【问题讨论】:

    标签: c# asp.net-mvc


    【解决方案1】:

    从 ASP.NET MVC 2 开始,当从另一个操作方法调用 return View() 时,没有任何这样的 API 调用来维护原始操作方法的 URL。

    因此,在 ASP.NET MVC 中推荐的解决方案和普遍接受的约定是有一个相应的、类似命名的操作方法,它只接受 HTTP POST 动词。因此,在您的情况下,使用另一个名为 Detail 的操作方法应该可以解决您在验证失败时使用不同 URL 的问题。

    [HttpPost]
    public ActionResult Detail(Application entity)
    {
        base.Update(entity);
        if (ModelState.IsValid)
        {
            //Save the entity here
        }
       return View("Detail", new { id = entity.Id });
    }  
    

    此解决方案符合 ASP.NET MVC 最佳实践,也避免了使用 modestatetempdate

    此外,如果您还没有探索过此选项,那么 asp.net mvc 中的 客户端验证 也可能会针对您的 URL 问题提供一些解决方案。我强调 some 因为当浏览器上禁用 javascript 时,这种方法将不起作用。

    因此,最好的解决方案是使用名为 Detail 但只接受 HTTP POST 动词的操作方法。

    【讨论】:

    • @Bikal Gurung - 谢谢。 .我一直认为这是一种奇怪的做法,因为动作的名称并不能真正代表你在做什么,但我同意这会解决我所问的问题
    • 你可以用 [ActionName("Details")] public ActionResult WhatEverMethodName() 来解决这个问题 现在该方法将对应于“/Details”
    • “此解决方案符合 ASP.NET MVC 最佳实践...” - 实际上,我认为它符合最佳实践。从 POST 调用返回视图直接与 PRG 模式相冲突,这是整个 Web 的最佳实践,而不仅仅是 ASP.NET MVC。在 ASP.NET MVC 中有很多方法可以遵循 PRG 模式,但最常见的方法是已经提到的 ModelState->TempData->ModelState 方法。您可以在此处阅读有关 PRG 的更多信息:en.wikipedia.com/wiki/Post/Redirect/Get 以及在tinyurl.com/39p54k8 处具有属性的良好实现@
    • @Tomas - PRG 模式在这里不相关。 PRG 应用的用例主要是防止重复提交表单。引用您提到的同一链接“...发布/重定向/获取(PRG)是Web开发人员帮助避免某些重复表单提交的常见设计模式...”。我们不希望在此处阻止重复的表单提交,只是保留原始 URL。甚至 PRG 提到的问题也可以通过通过 javascript 禁用“提交”按钮来实现。使用 ModelState->TempData->ModelState 只是为了保留原始 URL 不是一个直观的解决方案,而且是一种矫枉过正
    • @Bikal:实际上,您在帖子中所做的是建议几种反模式来解决一个简单的问题。这个问题可以通过实现许多人推荐的模式来解决——以“顺便”的方式——解决或防止未来可能出现的多个其他问题。您声称以这种方式使用 TempData 是不好的做法 - 为什么?这正是TempData 存在的工作类型。很抱歉,您的解决方案是我见过的最糟糕的解决方案之一。
    【解决方案2】:

    这里的问题实际上是由您的实现引起的。这并不能回答您的问题,但它首先描述了您哪里出错了。

    如果您想要一个用于更新或编辑项目的页面,则 URL 应反映这一点。例如。

    您访问 http:\www.mysite.com\App\Detail 并显示一些关于某事的信息。这就是 URL 描述的它要做的事情。在您的控制器中,Detail() 方法将返回 Detail.aspx 视图。

    要编辑项目,您可以访问 http:\www.mysite.com\App\Edit 并更改您希望更新的信息,表单将回发到相同的 URL - 您可以在控制器中使用这些方法:

    [HttpGet]
    public ActionResult Edit() {
        MyModel model = new MyModel();
        ...
        return View(model);
    }
    
    [HttpPost]
    public ActionResult Edit(MyModel model) {
        ...
        if (ModelState.IsValid) {
            // Save and redirect
            ...
            return RedirectToAction("Detail");
        }
        return View(model);
    }
    

    如果你发现自己在这样做......

    return View("SomeView", model);
    

    您让自己的生活变得更加艰难(同时也违反了 URL 背后的原则)。

    如果您想重用视图的一部分,请将其设为局部视图,并将其呈现在以控制器上的方法命名的视图内。

    很抱歉,这可能不是很有帮助,但是通过显示来自不同命名方法的相同视图,您陷入了 MVC 反模式陷阱。

    【讨论】:

      【解决方案3】:

      正如@Malcolm sais 所说,最佳实践是将ModelState 放入TempData,但不要手动操作!如果您在每个相关的控制器操作中手动执行此操作,则会引入大量重复代码,并大大增加维护成本。

      相反,实现几个 属性 为您完成这项工作。 Kazi Manzur has an approach(向下滚动到帖子末尾)已被广泛传播,Evan Nagle 展示了一个与 Kazi 基本相同的implementation with tests,但名称不同。由于他还提供确保属性有效的单元测试,因此在您的代码中实现它们将意味着很少或没有维护成本。您唯一需要跟踪的是控制器操作使用适当的属性进行修饰,这些属性也可以进行测试。

      当您拥有适当的属性时,您的控制器可能看起来像这样(我故意简化,因为我不知道您继承的类):

      [HttpPost, PassState]
      public ActionResult Update(EntityType entity)
      {
          // Only update if the model is valid
          if (ModelState.IsValid) {
              base.Update(entity);
          }
          // Always redirect to Detail view.
          // Any errors will be passed along automagically, thanks to the attribute.
          return RedirectToAction("Detail", new { id = entity.Id });
      }
      
      [HttpGet, GetState]
      public ActionResult Detail(int id)
      {
          // Get stuff from the database and show the view
          // The model state, if there is any, will be imported by the attribute.
      }
      

      您提到您觉得将ModelState 放入TempData 感觉就像是“黑客” - 为什么?我同意你的观点,在每个控制器操作中使用重复的代码来做这件事似乎很笨拙,但这不是我们在这里所做的。事实上,this is exactly what TempData is for。而且我不认为上面的代码看起来很老套……你呢?

      虽然这个问题的解决方案可能看起来更简单,例如只是重命名操作方法以保留 URL,但我强烈建议不要使用这种方法。它确实解决了 this 问题,但引入了其他一些问题 - 例如,您仍然无法防止双重表单提交,并且您将拥有相当混乱的操作名称(调用 @ 987654330@ 实际上更改服务器上的东西)。

      【讨论】:

        【解决方案4】:

        您要求的最佳实践实际上是您解释不做的事情:将模型状态放入临时数据中。 Tempdata 是为此而生的,这就是为什么我不会称它为 hack。

        如果这是很多重复代码,您可以使用 MVCContrib 的属性 modeldatatotempdata。但 store 仍然是 TempData。

        【讨论】:

          猜你喜欢
          • 2010-10-03
          • 1970-01-01
          • 1970-01-01
          • 2018-10-14
          • 1970-01-01
          • 2017-05-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多