部分视图和验证(回发)

时间:2011-04-11 10:28:04

标签: asp.net-mvc validation asp.net-mvc-3 postback partial-views

标题可能不那么清楚(因为我找不到更好的标题)但我想弄清楚的是当你有一个正常(而不是部分)视图时,通常会有一个GET动作方法它只是使用视图模型的新实例呈现视图,以及接受视图模型实例作为参数的POST操作方法(通常具有相同名称)。在POST操作方法中,检查ModelState是否有效,你做了你应该做的事情,如果没有你使用相同的视图模型实例再次渲染视图,以显示任何错误。

这实际上是我对ASP.NET MVC非常喜欢的事情之一,但它如何与部分视图一起使用?如果我使用视图模型的实例渲染局部视图,它仅在整个Web应用程序的上下文中显示具有白色背景的局部视图。如果我回发传递视图模型实例的普通视图,则会导致StackOverflowException。

以下是一个例子:

    public ActionResult Login()
    {
        return PartialView(new LoginViewModel());
    }

    [HttpPost]
    public ActionResult Login(LoginViewModel dto)
    {
        bool flag = false;
        if (ModelState.IsValid)
        {
            if (_userService.AuthenticateUser(dto.Email, dto.Password, false)) {
                var user = _userService.GetUserByEmail(dto.Email);
                var uSession = new UserSession
                {
                    ID = user.Id,
                    Nickname = user.Nickname
                };
                SessionManager.RegisterSession(SessionKeys.User, uSession);
                flag = true;

                if(dto.RememberMe)
                {
                    _appCookies.Email = dto.Email;
                    _appCookies.Password = dto.Password;
                }
            }
        }
        if (flag)
            return RedirectToAction("Index", "Home");
        else
        {
            ViewData.Add("InvalidLogin", "The login info you provided were incorrect.");
            return View(dto); //causes a StackOverflowException
        }
    }

更新:以下是登录视图:

@inherits ModelWebViewPage<Sharwe.MVC.ViewModels.LoginViewModel>

<div class="userFormHeader"><h2>Login</h2></div>
<div id="loginBox">
    @using(Html.BeginForm("Login", "User", FormMethod.Post))
    {
        @Html.ValidationSummary(true)

        <div id="loginFormFields">
            <div class="display-field">@this.TextBox(m => m.Email).Class("emailField").Attr("rel", "email").Class("autoText")</div>

            <div class="display-field">@this.TextBox(m => m.Password).Class("passwordField").Attr("rel", "password").Class("autoText")</div>

            <div>@this.CheckBox(m => m.RememberMe) <span class="smallText">remember me</span></div>
        </div>

        <div id="loginFormActions">
            <div><input type="submit" id="loginSubmit" class="okButton" name="loginSubmit" value="Ok" /></div>
            <div> @this.Html.ActionLink("forgot password", "ForgotPassword", "User", new { @class = "verySmallText" } )</div>
        </div>
    }
</div>

那我该怎么做呢?有什么建议?

更新: (在Darin的回答之后)

以下是我的登录操作方法现在的样子:

    [HttpPost]
    public ActionResult Login(LoginViewModel dto)
    {
        bool flag = false;
        if (ModelState.IsValid)
        {
            if (_userService.AuthenticateUser(dto.Email, dto.Password, false))
            {
                var user = _userService.GetUserByEmail(dto.Email);
                var uSession = new UserSession
                {
                    ID = user.Id,
                    Nickname = user.Nickname
                };
                SessionManager.RegisterSession(SessionKeys.User, uSession);
                flag = true;

                if (dto.RememberMe)
                {
                    //create the authentication ticket
                    var authTicket = new FormsAuthenticationTicket(
                        1,
                        user.Id.ToString(), //user id
                        DateTime.Now,
                        DateTime.Now.AddMinutes(20), // expiry
                        true, //true to remember
                        "", //roles 
                        "/"
                        );

                    //encrypt the ticket and add it to a cookie
                    var cookie = new HttpCookie(FormsAuthentication.FormsCookieName,
                                                FormsAuthentication.Encrypt(authTicket));
                    Response.Cookies.Add(cookie);
                }
            }
        }
        if (flag)
        {
            return Json(new { redirectTo = Url.Action("Index", "Home") });
        }
        else
        {
            ViewData.Add("InvalidLogin", "The login info you provided were incorrect.");
            return PartialView(dto);
        }
    }

1 个答案:

答案 0 :(得分:2)

正如您在评论部分所述,AJAX是您的选择,您可以通过AJAX化登录表单继续操作。 jquery form plugin非常适合这项工作,我强烈推荐它。

因此,您可以在登录视图中为登录表单提供ID:

@inherits ModelWebViewPage<Sharwe.MVC.ViewModels.LoginViewModel>
<div class="userFormHeader"><h2>Login</h2></div>
<div id="loginBox">
    @using(Html.BeginForm("Login", "User", null, FormMethod.Post, new { id = "loginForm" }))
    {
        ...
    }
</div>

然后包含一个javascript,它将AJAxify这个表单:

$(function() {
    ajaxifyLoginForm();
});

function ajaxifyLoginForm() {
    $('#loginForm').ajaxForm({
        success: function(html) {
            $('#loginBox').html(html);
            ajaxifyLoginForm();
        }
    });
}

现在剩下的就是从Login控制器动作返回部分视图,以防出现错误:

return PartialView(dto);

我们也需要处理成功案例。这可以通过返回JSON字符串来完成:

return Json(new { redirectTo = Url.Action("Index", "Home") });

然后调整客户端脚本:

function ajaxifyLoginForm() {
    $('#loginForm').ajaxForm({
        success: function(data) {
            if (data.redirectTo != null && data.redirectTo != '') {
                // Everything went fine => the user is successfully 
                // authenticated let's redirect him
                window.location.href = data.redirectTo;
            } else {
                // the model state was invalid or the user entered incorrect
                // credentials => refresh the login partial in the DOM and
                // reajaxify the form:
                $('#loginBox').html(data);
                ajaxifyLoginForm();
            }
        }
    });
}