Before I ask the question let me just clarify that the objective of this exercise is research/proof of concept so answers about how the design could be improved or that there are easier ways to accomplish this, though appreciated, might not help to fulfill the objective.
I am modifying the template of an ASP .Net MVC 4 application so the log in functionality is implemented with an ajax request that only updates the partial view that contains the log in controls and leaves the rest of the page undisturbed.
So far what I’ve done is the following:
I’ve added a form on the partial view so that it can be posted and substituted the action link with a button
@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>
}
}
I modified the action like so
[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;
}
And I created this small Javascript file
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);
});
The log in per se works. The POST action method is being called and the credentials are validated. The problem is that the partial view is not being updated and I have to force a post back by going to another page so that I can see the partial as if the user is logged in.
What would be the missing ingredient to accomplish this?
Thank you.
Two possible issues/solutions:
login– make sure your form has that id or wrap it in a div..load–$("section").load("/Account/Login #login", data)– ensure that a POST is being made (it should sincedatais an object)$.ajax("/Account/Login", { data: data, type: 'POST', dataType: 'html' }).done(function(result) { $("#login").empty().append(result); });EDIT:
If you carefully examine the HTTP headers being exchanged, you’ll notice that the initial call to retrieve that login partial view results in a 200 (OK) along with the login page instead of the expected 401 (unauthorized without the
[AllowAnonymous]attrib). Hence, the response HTML is not the partial you expected, but a full html document. Your script doesn’t notice that though, and continues normally. To fix that, you’ll need to modify how your app responds to unauthenticated requests. There are a number of different things you can do, and fortunately for us all, Phil Haack blogged about them a while back. His post can be found here.