【问题标题】:ASP.NET MVC - How to maintain ModelState from a different controller?ASP.NET MVC - 如何从不同的控制器维护 ModelState?
【发布时间】:2011-05-30 04:03:39
【问题描述】:

我有一个 HomeController 带有一个显示 Index.aspx 视图的 Index 操作。它有一个用户名/密码登录部分。当用户单击提交按钮时,它会 POST 到 AccountController 中的登录操作。

        <% Html.BeginForm("Login", "Account", FormMethod.Post); %>

在该操作中,它测试用户名/密码的有效性,如果无效,则将用户发送回登录页面,并显示凭据错误的消息。

    [HttpPost]
    public ActionResult Login(LoginViewModel Model, string ReturnUrl)
    {
        User user = MembershipService.ValidateUser(Model.UserName, Model.Password);
        if (user != null)
        {
            //Detail removed here
            FormsService.SignIn(user.ToString(), Model.RememberMe);
            return Redirect(ReturnUrl);
        }
        else
        {
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
        }
        // If we got this far, something failed, redisplay form
        return RedirectToAction("Index", "Home");  // <-- Here is the problem.  ModelState is lost.
    }

但问题是:ValidationSummary 始终为空白,因为在 RedirectToAction 时我们会丢失模型。

所以问题是:如何在没有重定向的情况下将用户发送到不同控制器上的操作?

【问题讨论】:

    标签: asp.net-mvc validation redirect login


    【解决方案1】:

    正如其他人所说,如果验证失败,通常会返回视图,但是当您从帐户控制器调用时,您需要指定视图的完整路径

    return View("~/Views/Home/Index.aspx", model);
    

    或者

    拥有一个单独的登录页面并在登录失败时重定向到该页面也很常见。两个页面都将提交相同的登录操作。例如,Facebook 就是这样做的。

    或者

    因为你只想显示错误信息

    return RedirectToAction("Index", "Home", new { LoginAttempts = 1 });
    

    然后在您的索引操作中读取LoginAttempts 参数并选择相应地显示错误消息。

    【讨论】:

    • 您的选项 1 是最容易实现的,并且完全符合我的需要。谢谢。
    【解决方案2】:

    使用TempData 保存请求之间的状态。为方便起见,请使用特殊属性,如 here 所示。

    值得一提的时刻:

    • 不要直接从您的 POST 操作返回 View,尊重 Post-Redirect-Get 模式。
    • 不要过度使用TempData应该在重定向之前保存模型状态并在重定向后立即检索它。

    【讨论】:

    • Post-Redirect-Get 仅在操作成功时至关重要。当它失败时,通常的做法是不使用 PRG 模式。刷新请求不会以任何形式或形式损害系统......在这种特殊情况下,它会失败。
    • 好吧,罗伯特,也许这在某种程度上是个人喜好问题,但我更喜欢 PRG,因为每当您有机会看到浏览器模式窗口询问您是否要重新发布时。我更喜欢更流畅和“历史友好”的用户体验。也许“登录”有点特殊,但在常规表单中,我也更喜欢只在该页面上显示表单,其 URL 以“编辑”而不是“更新”结尾(是的,我不为两者使用相同的操作名称)。
    • 如果在失败的情况下执行 PRG,如何为用户保留模型状态错误?
    • @user6130,如果我理解你的问题,那么这正是TempData 发挥作用的地方。我在重定向到 GET 方法之前将无效的 ModelState 保存在那里,并在重定向后在该方法中应用 ModelState(因此我很容易在初始表单上向用户显示错误以及用户输入的所有数据)。这正是 TempData 的用途。我为此使用属性,因此我不必通过那些繁琐的重复 ModelState 检查来污染我的控制器操作。如果您有特定的部署要求,您甚至可以实现自己的 ITempDataProvider。有意义吗?
    • @Vasilio 我已经看到了这种模式,并且所有临时数据检查都比模型状态检查 IMO 更加混乱,因为至少 ModelState 有一些结构,而不仅仅是 TempData 中松散类型的对象。此外,如果由于某些网络侥幸而导致重定向失败,并且用户尝试刷新页面或在 URL 栏中按 Enter 以获取新的空表单,但您的代码却看到了一些 tempdata[key] != null,并在他们甚至没有尝试提交该表单时,给他们一个包含一些数据和错误消息的表单。
    【解决方案3】:

    你总是可以这样做

    return View("~/Views/Home/Index.aspx", myModel);
    

    这不是真正的重定向,客户端 url 仍将指向 /login/ 但至少你有你的 modalstate

    【讨论】:

    • 因为这是从帐户控制器的登录操作中调用的,这将返回 Views/Account 文件夹中的 Index 视图,而不是 Views/Home 文件夹中的 Index 视图
    • 你说得对,没想到。不过这很容易解决。谢谢
    【解决方案4】:

    三个选项

    1. 您可以直接调用该操作,但客户端不会更改其 URL。因此,您可以直接调用HomeController 类的Index() 方法,而不是调用RedirectToAction

      HomeController c = new HomeController();
      c.ViewData = this.ViewData;
      return c.Index(data);
      

      这个有点棘手。除了ViewData 所需的ModelState 之外,您可能还需要设置其他内容。

    2. 您也可以使用 TempData 字典并用您想要的任何数据填充它并使用它。

    3. 提供视图完整路径的最简单方法

      return View("~/Views/Home/Index.aspx", data);
      

    大玩家使用的更好的建议

    如果我们看看其他网站是如何做这种情况的。以Twitter 为例(正如@David 所说,Facebook 显然也是如此)。您可以从Home/Index 操作登录(可以这么说,如果它是使用 Asp.net MVC 开发的)。但是当登录失败时,它会显示一个单独的登录页面,显示验证错误。在您的情况下,它将是Account/SignIn。这是有道理的,您可以直接返回带有验证错误的视图。当一切都好时,你会像现在一样做。重定向回Home/Index

    【讨论】:

    • 视图名称、母版页名称和模型的视图(字符串、字符串、对象)重载不是视图名称、控制器名称和模型吗?
    • 你是对的。让我调整一下我的答案。但我还没有检查它是否有效。做它并在它还不起作用时提供反馈。
    • 我试过了。大卫是对的。您修改后的答案也有效,但除非您像其他一些人建议的那样在视图名之后添加模型,否则不会保持状态。 +1 大玩家使用的更好建议,顺便说一句。很好的建议。
    • 这与“大玩家”所做的不同之处在于,上面不会更改 URL,因为它不是重定向。因此,在更正错误后从浏览器提交的表单可能会执行错误操作。
    【解决方案5】:

    尝试使用

    return View("Index", "Home", Model)
    

    【讨论】:

    • "Home" 将是此处母版页的名称,而不是控制器的名称。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-16
    • 2010-09-21
    • 1970-01-01
    • 1970-01-01
    • 2021-09-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多