部分视图和验证(回发)

发布于 2024-10-31 07:09:15 字数 4525 浏览 3 评论 0原文

标题可能不太清楚(因为我找不到更好的标题),但我试图弄清楚的是,当你有一个普通(而不是部分)视图时,通常有一个 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);
        }
    }

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

半﹌身腐败 2024-11-07 07:09:15

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

因此,您可以在登录视图中向登录表单提供一个 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();
            }
        }
    });
}

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();
            }
        }
    });
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文