On my forgot password form, the jquery ajax post is fired multiple times (one more after each submit). So if the user enters an invalid email 3 times, and then a valid email, the user gets 4 password-changed emails. Seems like the event gets stacked each time an error is thrown, and all of them get fired on the next submit. Here's all my relevant code. What am I doing wrong?
JS
RetrievePassword = function () {
var $popup = $("#fancybox-outer");
var form = $popup.find("form");
form.submit(function (e) {
var data = form.serialize();
var url = form.attr('action');
$.ajax({
type: "POST",
url: url,
data: data,
dataType: "json",
success: function (response) {
ShowMessageBar(response.Message);
$.fancybox.close()
},
error: function (xhr, status, error) {
ShowMessageBar(xhr.statusText);
}
});
return false;
});
};
MVC CONTROLLER/ACTION
[HandlerErrorWithAjaxFilter, HttpPost]
public ActionResult RetrievePassword(string email)
{
User user = _userRepository.GetByEmail(email);
if (user == null)
throw new ClientException("The email you entered does not exist in our system. Please enter the email address you used to sign up.");
string randomString = SecurityHelper.GenerateRandomString();
user.Password = SecurityHelper.GetMD5Bytes(randomString);
_userRepository.Save();
EmailHelper.SendPasswordByEmail(randomString);
if (Request.IsAjaxRequest())
return Json(new JsonAuth { Success = true, Message = "Your password was reset successfully. We've emailed you your new password.", ReturnUrl = "/Home/" });
else
return View();
}
HandlerErrorWithAjaxFilter
public class HandleErrorWithAjaxFilter : HandleErrorAttribute
{
public bool ShowStackTraceIfNotDebug { get; set; }
public string ErrorMessage { get; set; }
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
var content = ShowStackTraceIfNotDebug || filterContext.HttpContext.IsDebuggingEnabled ? filterContext.Exception.StackTrace : string.Empty;
filterContext.Result = new ContentResult
{
ContentType = "text/plain",
Content = content
};
string message = string.Empty;
if (!filterContext.Controller.ViewData.ModelState.IsValid)
message = GetModeStateErrorMessage(filterContext);
else if (filterContext.Exception is ClientException)
message = filterContext.Exception.Message.Replace("
", " ").Replace("
", " ");
else if (!string.IsNullOrEmpty(ErrorMessage))
message = ErrorMessage;
else
message = "An error occured while attemting to perform the last action. Sorry for the inconvenience.";
filterContext.HttpContext.Response.Status = "500 " + message;
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
else
{
base.OnException(filterContext);
}
}
private string GetModeStateErrorMessage(ExceptionContext filterContext)
{
string errorMessage = string.Empty;
foreach (var key in filterContext.Controller.ViewData.ModelState.Keys)
{
var error = filterContext.Controller.ViewData.ModelState[key].Errors.FirstOrDefault();
if (error != null)
{
if (string.IsNullOrEmpty(errorMessage))
errorMessage = error.ErrorMessage;
else
errorMessage = string.Format("{0}, {1}", errorMessage, error.ErrorMessage);
}
}
return errorMessage;
}
}
Here's more JS. I'm using the fancybox plugin as my lightbox. The Retrieve Password link is on the login lightbox.
$(document).ready(function () {
$(".login").fancybox({
'hideOnContentClick': false,
'titleShow': false,
'onComplete': function () {
$("#signup").fancybox({
'hideOnContentClick': false,
'titleShow':false,
'onComplete': function () {
}
});
$("#retrievepassword").fancybox({
'hideOnContentClick': false,
'titleShow': false,
'onComplete': function () {
}
});
}
});
});
See Question&Answers more detail:
os