笑故挽风 2015-09-07 15:16 采纳率: 100%
浏览 119

从MVC登录Ajax

I have an MVC project that uses the inbuilt forms authentication (which talks to the MDF database stored in App_data). I want to change the login form to be the Ajax login form so that I can take advantage of the "onSuccess" and "onFailure" options.

Does anyone have a working example of how I would achive this as I've tried previuosly but I can't get the form to authenticate it just does nothing. I think I may have missed a step so any help is appreciated.

Example code would also be benificial. Please find my current code below.

The login view

@model MyProject.Models.LoginViewModel

@using (Ajax.BeginForm("Login", "Account", null, new AjaxOptions
    {
        OnSuccess = "OnSuccess",
        OnBegin = "OnBegin",
        OnFailure = "OnFailure",
        OnComplete = "OnComplete",
        HttpMethod = "POST",
        UpdateTargetId = "target"   
    }))
    {
        @Html.AntiForgeryToken()
        @Html.ValidationSummary(true)
        <fieldset>
          <legend>Login Form</legend>
            <ol>
              <li>
                @Html.LabelFor(m => m.UserName)
                @Html.TextBoxFor(m => m.UserName)
                @Html.ValidationMessageFor(m => m.UserName)
              </li>
              <li>
                @Html.LabelFor(m => m.Password)
                @Html.PasswordFor(m => m.Password)
                @Html.ValidationMessageFor(m => m.Password)
              </li>
              <li>
                @Html.CheckBoxFor(m => m.RememberMe)
                @Html.LabelFor(m => m.RememberMe, new { @class = "checkbox" })
              </li>
            </ol>
            <input type="submit" value="Login" />
          </fieldset>
}

Here is the login controller

 [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public JsonResult ValidateUser(string userid, string password,
                                   bool rememberme)
    {
        LoginStatus status = new LoginStatus();
        if (Membership.ValidateUser(userid, password))
        {
            FormsAuthentication.SetAuthCookie(userid, rememberme);
            status.Success = true;
            status.TargetURL = FormsAuthentication.
                               GetRedirectUrl(userid, rememberme);
            if (string.IsNullOrEmpty(status.TargetURL))
            {
                status.TargetURL = FormsAuthentication.DefaultUrl;
            }
            status.Message = "Login attempt successful!";
        }
        else
        {
            status.Success = false;
            status.Message = "Invalid UserID or Password!";
            status.TargetURL = FormsAuthentication.LoginUrl;
        }
        return Json(status);
    }

Here is the login view model

    public class LoginStatus
{
    public bool Success { get; set; }
    public string Message { get; set; }
    public string TargetURL { get; set; }
}

Script on the page for handling the form

$(document).ready(function () {
    $("#login").click(function () {

        $("#message").html("Logging in...");

        var data = {
            "UserName": $("#userid").val(),
            "Password": $("#password").val(), 
            "RememberMe": $("#rememberme").prop("checked")
            };
        $.ajax({
            url: "/Home/Index",
            type: "POST",
            data: JSON.stringify(data),
            dataType: "json",
            contentType: "application/json",
            success: function (status) {
                $("#message").html(status.Message);
                if (status.Success)
                {
                    window.location.href = status.TargetURL;
                }
            },
            error: function () {
                $("#message").html("Error while authenticating user credentials!");
            }
        });
    });
});
  • 写回答

1条回答 默认 最新

  • weixin_33701251 2015-09-07 16:02
    关注

    I've an extensions (MvcNotification) that put into ViewData or TempData messages to display.

    To complement this, my post actions return "ERROR" or "OK" and i use those messages inside the ajax form OnSuccess.

    MessageType

    public enum MessageType
    {
        Success,
        Warning,
        Error,
        Info    
    }
    

    AjaxMessagesFilter

    /// <summary>
    /// If we're dealing with ajax requests, any message that is in the view     data goes to the http header.
    /// </summary>
    public class AjaxMessagesFilter : ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                var viewData = filterContext.Controller.ViewData;
                var response = filterContext.HttpContext.Response;
    
                foreach (var messageType in Enum.GetNames(typeof(MessageType)))
                {
                    var message = viewData.ContainsKey(messageType)
                                ? viewData[messageType]
                                : null;
                    if (message != null) // We store only one message in the http header. First message that comes wins.
                    {
                        response.AddHeader("X-Message-Type", messageType.ToLower());
                        response.AddHeader("X-Message", HttpUtility.HtmlEncode(message.ToString()));
                        return;
                    }
                }
            }
        }
    }
    

    ControllerExtensions

    public static class ControllerExtensions
    {
        public static ActionResult ShowMessage(this Controller controller, MessageType messageType, string message, bool showAfterRedirect = false, bool UseJson = false)
        {
            var messageTypeKey = messageType.ToString();
            if (showAfterRedirect)
            {
                controller.TempData[messageTypeKey] = message;
            }
            else
            {
                controller.ViewData[messageTypeKey] = message;
            }
    
            if (UseJson)
                return new JsonResult() { Data = "ERROR", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
            else
                return new ContentResult() { Content = "ERROR" };
        }
    
        public static ActionResult ShowMessage(this ControllerBase controller, MessageType messageType, string message, bool showAfterRedirect = false, bool UseJson = false)
        {
            var messageTypeKey = messageType.ToString();
            if (showAfterRedirect)
            {
                controller.TempData[messageTypeKey] = message;
            }
            else
            {
                controller.ViewData[messageTypeKey] = message;
            }
    
            if (UseJson)
                return new JsonResult() { Data = "ERROR", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
            else
                return new ContentResult() { Content = "ERROR" };
        }
    
        public static ActionResult EmptyField(this Controller controller, string FieldName, bool IsJson = false)
        {
            controller.ShowMessage(MessageType.Info, String.Format("O campo \"{0}\" é de carácter obrigatório.", FieldName));
            return IsJson == false ? (ActionResult)new ContentResult() { Content = "ERROR" } : (ActionResult)new JsonResult() { Data = "ERROR", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        }
    }
    

    To call the extension inside the controller:

    this.ShowMessage(MessageType.Error, "An error has occurred");
    

    if you want to redirect after the message is thrown, you need to add true in the last parameter.

    this.ShowMessage(MessageType.Error, "An error has occurred", true);
    

    Note: I created the EmptyField method to give a standart message when some field is empty.

    Action Example (LoginPost)

    [HttpPost]
    [AllowAnonymous]
    public ActionResult LoginPost(LoginViewModel model, string returnUrl, bool Relogin = false)
    {
        returnUrl = string.IsNullOrEmpty(returnUrl) || string.IsNullOrWhiteSpace(returnUrl) ? "/" : returnUrl;
    
        if (string.IsNullOrEmpty(model.UserName))
            return this.EmptyField(Resource_General.UserName);
    
        if (string.IsNullOrEmpty(model.Password))
            return this.EmptyField(Resource_General.Password);
    
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, change to shouldLockout: true
        var result = SignInManager.PasswordSignIn(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
    
        switch (result)
        {
            case SignInStatus.Success:
                var user = db.Users.FirstOrDefault(x => x.UserName == model.UserName);
    
                if (!user.IsActive)
                {
                    AuthenticationManager.SignOut();
                    this.ShowMessage(MessageType.Error, Messages.LockedOutUser);
                    return Content("ERROR");
                }
    
                if (Url.IsLocalUrl(returnUrl))
                    return Content(returnUrl);
                else
                    return Content("/Home");
            case SignInStatus.LockedOut:
                this.ShowMessage(MessageType.Error, Messages.LockedOutUser);
                return Content("ERROR");
            case SignInStatus.RequiresVerification:
            case SignInStatus.Failure:
            default:
                this.ShowMessage(MessageType.Error, Messages.WrongPassword);
                return Content("ERROR");
        }
    }
    

    Ajax Form

    @using (Ajax.BeginForm("LoginPost", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, new AjaxOptions { OnSuccess = "OnSuccess" }, new { @id = "login-form" }))
    {
        @Html.AntiForgeryToken()
    
        <div class="network-login-fields">
            <div class="form-group">
                <div class="input-group col-xs-12">
                    @Html.TextBoxFor(m => m.UserName, new { @class = "form-control", placeholder = Resource_General.UserNamePlaceHolder, name = "loginname", autofocus = "true" })
                </div>
            </div>
    
            <div class="form-group">
                <div class="input-group col-xs-12">
                    @Html.PasswordFor(m => m.Password, new { @class = "form-control", placeholder = Resource_General.PasswordPlaceHolder, name = "password" })
    
                </div>
            </div>
    
            <div class="network-login-links">
                <button class="btn btn-default"><i class="fa fa-sign-in"></i>&nbsp;@Resource_General.Login</button>
            </div>
        </div>
    }
    

    Javascript

    function OnSuccess(data) {
        if (data != "ERROR") {
            window.location.href = data;
        }
    }
    

    Here in the javascript, you need to handle the ajax form OnSuccess and do something if the response is "OK" or "ERROR".

    In your main javascript file you need to include this:

    Handle Messages

       // START Messages and Notifications
    function handleAjaxMessages() {
        $(document).ajaxStart(function () {
            Openloading();
        }).ajaxComplete(function (e, xhr, settings) {
            CloseLoading();
        }).ajaxSuccess(function (event, request) {
            checkAndHandleMessageFromHeader(request);
        }).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {
            if (thrownError !== "abort") {
                CloseLoading();
                NotifyError();
            }
            OnInit();
        });
    }
    
    function checkAndHandleMessageFromHeader(request) {
        var msg = request.getResponseHeader('X-Message');
        if (msg) {
            var title = NotifyHeader(request.getResponseHeader('X-Message-Type'));
            Notify(msg, title, request.getResponseHeader('X-Message-Type'));
        }
    }
    
    function NotifyHeader(type) {
        console.log(type);
        var title = "";
        if (type == "error")
            title = CustomScriptsLocales.ErrorTitle;
        if (type == "success")
            title = CustomScriptsLocales.SuccessTitle;
        if (type == "warning")
            title = CustomScriptsLocales.WarningTitle;
        if (type == "info")
            title = CustomScriptsLocales.InfoTitle;
        console.log(title);
        return title;
    }
    
    function Notify(message, title, type) {
        if (title == null || title == "" || title == undefined) {
            title = NotifyHeader(type);
        }
    
        PNotify.desktop.permission();
        var notice = new PNotify({
            title: title,
            text: decodeHtml(message),
            nonblock: {
                nonblock: true,
                nonblock_opacity: .55
            },
            buttons: {
                closer: true,
            },
            desktop: {
                desktop: false,
            },
            hide: true,
            type: type,
            delay: 2000,
            insert_brs: true,
            remove: true,
        });
    }
    
    function NotifyError() {
        Notify(CustomScriptsLocales.ErrorMessage, CustomScriptsLocales.ErrorTitle, "error");
    }
    // END Messages and Notifications
    

    And call it inside a ready function:

    $(document).ready(function () {
        handleAjaxMessages();
    }
    

    Note: I use the PNotify plugin to show notifications. If you don't want notifications just exclude all this javascript "Handle Messages".

    评论

报告相同问题?

悬赏问题

  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?