【问题标题】:ASP.Net MVC partial views keeping their model state?ASP.Net MVC 部分视图保持其模型状态?
【发布时间】:2011-07-06 06:13:27
【问题描述】:

这可能又是一个新手问题。

当我创建一个 ASP.NET MVC2 应用程序时,一个带有操作登录的帐户控制器是这样创建的:

[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
   if (ModelState.IsValid)
   {
      if (MembershipService.ValidateUser(model.UserName, model.Password))
      {
         FormsService.SignIn(model.UserName, model.RememberMe);
         if (!String.IsNullOrEmpty(returnUrl))
         {
            return Redirect(returnUrl);
         }
         else
         {
           return RedirectToAction("Index", "Home");
         }
       }
       else
       {
          ModelState.AddModelError("", "The user name or password provided is incorrect.");
       }
     }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

现在,我不想有登录页面,我希望将登录控件作为更大页面的一部分。因此,我将 Login.aspx 更改为 Login.ascx,并将其与 Html.RenderPartial 或 Html.RenderAction 集成到我的主视图中。

如果登录成功,两者都像一个魅力。如果不是,则

return View(model)

杀了我。 我想要的是回到我的主页(称之为主页/索引),但带有部分视图的错误信息。

return RedirectToAction("Index", "Home")

显然不起作用。

提示?

【问题讨论】:

    标签: asp.net-mvc partial-views


    【解决方案1】:

    这当然不是一个新手问题,我已经在网上上下梳理了这个问题的答案,到目前为止,我发现的最佳解决方案有点隐藏在本教程 here 中。这就是 Darin Dimitrov 对 Ajax 更新的建议。我将总结该链接的重要部分以及为什么不容易修复:/

    基于怪异情人的 Ajax 刷新

    ajax 刷新的解决方案几乎取决于以下功能(奇怪的情人使用 ControllerContext,但它对我来说不存在,所以我有 ControllerExtension):

    ControllerExtension.RenderPartialViewToString(this,"mypartial", (object)model)
    

    这个函数将你的模型 + 模型状态重新渲染成一个 html 字符串。然后,您可以获取该字符串并将其以 json 对象的形式发送回某些 javascript 以刷新视图。我用了jquery,看起来是这样的,

    $(document).ready(function () {
        var partialViewUpdate = function (e) {
                e.preventDefault(); //no postback
                var partialDiv = $(this).parent(".partial");
                $.post($(this).attr("action"),
                       $(this).serialize(),
                       function (json) {
                       if (json.StatusCode != 0) {
                           // invalid model, return partial 
                           partialDiv.replaceWith(json.Content);
                       }
                       else if (json.Content != null && json.Content != "") {
                           window.location.replace(data.Content);
                       };
               });
    
        $(".partial").find("form")
                     .unbind('submit')
                     .live("submit", partialViewUpdate);
    };
    

    jquery解释:

    1. 查找包含我的部分 (class="partial") 的 div 并在该 div 中找到表单
    2. 使用该表单取消绑定任何其他“提交”事件(在取消绑定之前,我遇到了一些奇怪的重复提交错误)。
    3. 使用“live”,以便在替换内容后再次重新绑定
    4. 一旦我们输入函数 partialViewUpdate...
    5. 防止表单完成提交,以便全部由 ajax 处理。
    6. 获取包含我的部分的 div(稍后将使用它)
    7. 通过从表单 $(this).attr("action") 中获取 jquery post url 来设置它
    8. 采用表单(即我们的模型)并将其序列化以用于控制器函数 $(this).serialize()
    9. 创建将处理 ajax 返回值的函数。
    10. 我使用我自己的个人 json 对象,其中 StatusCode 1 是错误的。因此,如果它不好,那么我会采用 Content 中的内容,这是 RenderPartialViewToString 给我的字符串,我只需替换包含我的部分的 div 的内容。

    为什么它不能“正常工作”

    因此,partials 不仅仅适用于模型状态验证的原因是您不能使用 POST 返回 View(model),因为 MVC 会将其解析为部分视图 (login.ascx) 的路由地址部分嵌入的位置(index.aspx)。

    您也不能使用 RedirectAction(),因为这会将其发送到 (index.aspx) 控制器函数,这相当于清除所有内容并刷新 index.aspx 页面。但是,如果您使用 Chino 和 Thabaza 建议的 ActionFilter,那么当您的页面被刷新并且 login.ascx 控制器功能再次被触发时,它将获取该临时数据。但是,如果刷新页面会导致客户端代码(例如弹出模式)出现麻烦(即,如果您刷新弹出窗口消失了),则此方法不起作用。

    不是这样说的

    如果它“刚刚工作”,我会更喜欢,所以如果有人知道这样做的正确/更好的方法,请分享它!我仍然觉得 Ajax 刷新和 ActionFilter 解决方案不是一种干净的方式,因为它几乎使它看起来像带有表单的部分视图,如果没有某种“技巧”是不可能使用的。

    【讨论】:

      【解决方案2】:

      是的,redirecttoaction,但在临时数据中提供了一条错误消息,所以你应该做这样的事情

      TempData["errorMsg"] = "incorrect values provided";
      return RedirectToAction("Index", "Home")
      

      当然,在主索引中你应该有一个显示消息的 div

      <%= html.Encode(TempData["errorMsg"]) %>
      

      编辑 我看到您想维护可能有问题的模型状态,但您可以做的是在索引操作中传递模型状态或在临时数据中传递模型状态对象。然后您可以做的是检查对象中是否存在模型状态错误,以及是否存在检查字段并将错误添加到正确的字段。

      【讨论】:

      • 嗯,也许我可以把整个 LogOnModel 放在 TempData 中。但对我来说,这似乎是一个黑客。这里的干净解决方案是什么?
      • 是的,这是一个 hack,干净的解决方案是为它编写一个过滤器。看看这个问题和我的答案,例如stackoverflow.com/questions/2503032/… 在过滤器中,如果不重新添加值,则检查模型状态是否有效,当然最好将其放在自定义属性中,因为我认为您不需要这个在控制器的所有动作中。然后,您可以在要维护模型状态的操作上使用该属性。
      • 嗯。这就是 TempData 的用途。我的猜测是你不喜欢魔术字符串和缺乏强类型数据。这很公平,但它是一种风格选择,而不是 hack。
      【解决方案3】:

      您可以明确指定要返回的视图:

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

      这样会保留错误信息并呈现正确的视图。

      【讨论】:

      • 如果我这样做了,我会将 LogOnModel 传递给 Home/Index-View。那可能行不通。也许这个问题毕竟不是那么新手:)
      • @Sparhawk,你是对的。如果您返回 Home/Index 视图的模型怎么办? LogOn 视图模型应该是 Home/Index 视图模型的子集。作为替代方案,您可以 ajaxify 登录表单并异步调用此操作,如果成功,重定向将在客户端 (window.location.href) 上执行,如果出现错误,它将返回 PartialView 和脚本调用此操作将刷新包含此表单的 div
      • 目前它不是 Home/Index 视图模型的子集。我有一个门户,每个页面(如主页/索引)都有许多控件。我正在将带有 RenderAction 的控件渲染到页面中。那么,为什么 Home/Index 现在应该查看有关 LogonModel 的任何内容?当然,我可以使用 ajax。但这感觉就像在规避这个问题。对于带有验证逻辑的部分视图,没有干净的解决方案吗?
      【解决方案4】:

      看看这个blog 的练习#13。当您以 PRG(Post-Redirect-Get)样式进行编码时,此方法可以很好地传递模型状态信息。您只需要创建几个操作过滤器并将它们应用到您的 get 和 post 操作中。

      【讨论】:

        【解决方案5】:

        我在使用 Ajax.BeginForm 时遇到了同样的问题,我需要返回一个局部视图,但是所有的模型状态错误都消失了。 诀窍是将 Ajax.BeginForm 部分隔离到一个单独的视图中,并在另一个包含视图的 UpdateTargetId div 中渲染此视图。

        这样您可以在遇到模型错误时返回带有模型错误的视图模型,或者只显示您选择的一些成功消息(如果有)。 这是一个很棒的详细解释: http://xhalent.wordpress.com/2011/02/05/using-unobtrusive-ajax-forms-in-asp-net-mvc3/

        【讨论】:

          猜你喜欢
          • 2010-12-06
          • 2012-02-10
          • 2011-09-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-07
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多