1

在我问这个问题之前,让我澄清一下这个练习的目标是研究/概念证明,所以回答如何改进设计或者有更简单的方法来完成这个,虽然很感激,但可能无助于实现目标.

我正在修改 ASP .Net MVC 4 应用程序的模板,因此登录功能是通过 ajax 请求实现的,该请求仅更新包含登录控件的部分视图,并使页面的其余部分不受干扰。

到目前为止,我所做的如下:

我在局部视图上添加了一个表单,以便可以发布它并用按钮替换操作链接

@using (Html.BeginForm("Login", "Account", FormMethod.Post, new { ReturnUrl = ViewBag.ReturnUrl }))
{
    @Html.ValidationSummary(true)

    if (Request.IsAuthenticated)
     {
         <p>
             Hello, @Html.ActionLink(User.Identity.Name, "ChangePassword", "Account", routeValues: null, htmlAttributes: new {@class = "username", title = "Change password"})!
             @Html.ActionLink("Log off", "LogOff", "Account")
         </p>
     }
     else
     {
         <ul>
             <li>@Html.LabelFor(m => m.UserName)</li>
             <li>@Html.TextBoxFor(m => m.UserName)</li>
             <li>@Html.LabelFor(m => m.Password)</li>
             <li>@Html.PasswordFor(m => m.Password)</li>
             <li><input class="loginLink loginButton" type="button" value="Log in"/></li>
             <li>@Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new {id = "registerLink"})</li>
         </ul>
     }
}

我像这样修改了动作

[AllowAnonymous]
[HttpPost]
public ActionResult Login(LoginModel model, string returnUrl)
{
    ActionResult result = View("_LoginPartial", model);

    if (ModelState.IsValid)
    {
        if (Membership.ValidateUser(model.UserName, model.Password))
        {
            FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
            if (Url.IsLocalUrl(returnUrl))
            {
                result = Redirect(returnUrl);
            }
            else
            {
                result = View("_LoginPartial", model);
            }
        }
        else
        {
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
        }
    }

    return result;
}

我创建了这个小的 Javascript 文件

var login = {
    callLogin: function () {
        var userName = $("#UserName").val();
        var password = $("#Password").val();

        var data = { UserName: userName, Password: password };
        $("#login").load("/Account/Login", data);
    }
};

$(document).ready(function () {
    $(".loginButton").click(login.callLogin);
});

登录本身有效。正在调用 POST 操作方法并验证凭据。问题是部分视图没有更新,我必须通过转到另一个页面来强制回帖,以便我可以看到部分视图,就好像用户已登录一样。

实现这一目标的缺失要素是什么?

谢谢你。

4

3 回答 3

1

两个可能的问题/解决方案:

  1. 没有 ID 为的元素login- 确保您的表单具有该 ID 或将其包装在 div 中。
  2. 在调用中将选择器作为 url 参数的一部分传递给.load- $("section").load("/Account/Login #login", data)- 确保正在生成 POST(它应该data是一个对象)
  3. 使用 Firebug 或类似的工具检查网络流量,以验证是否正在进行 POST,并返回部分渲染。
  4. 回退到更手动的方法:
    $.ajax("/Account/Login", { data: data, type: 'POST', dataType: 'html' }).done(function(result) { $("#login").empty().append(result); });

编辑:

我仍然想知道如果我只在成功登录尝试后返回部分视图,为什么这不起作用。

如果您仔细检查正在交换的 HTTP 标头,您会注意到检索该登录部分视图的初始调用导致 200(OK)以及登录页面,而不是预期的 401(未经授权,没有[AllowAnonymous]属性)。因此,响应 HTML 不是您期望的部分,而是一个完整的 html 文档。您的脚本并没有注意到这一点,并正常继续。要解决此问题,您需要修改应用程序响应未经身份验证的请求的方式。您可以做很多不同的事情,幸运的是,对于我们所有人来说,Phil Haack 不久前在博客中谈到了它们。他的帖子可以在这里找到。

于 2012-08-16T21:05:21.497 回答
0

ajax 调用应该返回需要显示的 html 内容,并且您应用负载的元素应该是将其内容替换为 ajax 响应的元素。

我想我错过了 load 的作用,因为我没有太多使用它(支持 $.post),但仍然应该返回一个视图(模型)。RedirectToAction 做同样的事情,因为它将重定向到一个动作,该动作将呈现该动作的视图(从而返回 html 内容),因此它可以工作。

于 2012-08-16T21:26:48.290 回答
0

找到并回答虽然不理想但确实很酷的答案。

事实证明,控制器需要稍微改变一下:

[AllowAnonymous]
[HttpPost]
public ActionResult Login(LoginModel model, string returnUrl)
{
    ActionResult result = View("_LoginPartial", model);

    if (ModelState.IsValid)
    {
        if (Membership.ValidateUser(model.UserName, model.Password))
        {
            FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
            if (Url.IsLocalUrl(returnUrl))
            {
                result = Redirect(returnUrl);
            }
            else
            {
                //This is the only difference from the original action
                result = RedirectToAction("Index", "Home");
            }
        }
        else
        {
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
        }
    }

    return result;
}

唯一的区别是,如果身份验证成功,我可以调用 RedirectToAction("index", "Home") 并且部分将正确更新。

现在这是很酷且不理想的部分。这样做的原因是,即使 RedirectToAction 方法返回完整的 Index 视图,因为我感兴趣的部分现在有一个“form”元素,并且感谢 Josh 建议在 jQuery 加载中包含一个“form”选择器方法,在完整的 Index 视图中,只有 _LoginPartial 视图被加载到“login”元素中。

我认为这并不理想,因为很多不需要的 HTML 被返回和丢弃,这是相当大的开销。此外,这还不错,如果返回的“索引”视图碰巧在其他地方有自己的表单,那么这两个表单将加载到“登录”元素中,这可以通过在其中放置更具体的选择器来轻松解决加载方法。

我仍然想知道如果我只在成功登录尝试后返回部分视图,为什么这不起作用。

于 2012-08-16T22:02:05.247 回答