开发者

PartialViews And Validation (Postback)

The title might not be so clear (because I couldn't find a better one) but what I'm trying to figure out is when you have a normal (as opposed to partial) view, usually there's a GET action method which simply renders the view with a new instance of the view model, and a POST action method (usually with the same name) that accepts an instance of the view model as a parameter. Inside the POST action method, you check the ModelState if it's valid you do what you're supposed to do, if not you render the view again with the same view model instance in order to display any errors.

That's actually one of the things I really like about ASP.NET MVC, but how does that work with Partial Views? If I render back a partial view with the instance of the view model, it only displays the partial view with a white background, out of the context of the whole web application. And if I post back a normal View passing the instance of the view model, that causes a StackOverflowException.

Here's an example:

    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
        }
    }

UPDATE: Here's the login view:

@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>

So how should I do this? Any suggestions?

UPDATE: (after Darin's answer)

Here's how my Login action method now looks like:

    [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);
        }
    }


As you stated in the comments section that AJAX is an option for you here's how you could proceed by AJAXifying the login form. The jquery form plugin is excellent for this job and I would strongly recommend it.

So you could provide an id to the login form in the login view:

@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>

and then include a javascript which will AJAxify this form:

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

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

Now all that's left is to return a partial view from the Login controller action in case there is an error:

return PartialView(dto);

We need to handle the success case as well. This could be done by returning a JSON string:

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

and then adapt the client script:

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();
            }
        }
    });
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜