Andrew Andrew - 1 month ago 6
C# Question

ASP.NET MVC Post Controller Action Invoked multiple times

I have an MVC application which is a very simple form of 6 fields. The form data is saved to my database via Entity Framework. Most of the time the data saves properly without issue, but randomly duplicates are appearing. I first thought it was a EF issue with duplicates (sometimes 1,3,5 of the same record) being saved with a timestamp of less than a second separating them. But I ran a unit test and pulled out all of the same logic that is in my controller action to save the data and my data never gets duplicated. I have JQuery that disables the submit button so I know its not related to multi-clicks.

So my question: is it possible that my controllers POST action can get called numerous times through some unknown behavior? I cannot replicate the issue in VS when i set a breakpoint in my controller action where the data is being saved. I also find it difficult to replicate in general and is completely random. Has anyone come across this ever or have any suggestions? It is occuring in all browsers (FF, IE, Chrome). Thanks

I ran my application through fiddler and on occasion when i submit my form I do see 2 POST requests on 1 single button click.

Code

@model PersonViewModel
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()

<div class="form-horizontal">


@Html.ValidationSummary(false, "The following errors have occurred:", new { @class = "alert alert-danger", @role = "alert" })



<hr />

<p>Please enter your personal details below. (* denotes required field)</p>

<div class="form-group">
@Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control", @maxlength = "50", @title = "Legal First Name" } })
@Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" })

</div>
</div>

<div class="form-group">
@Html.LabelFor(model => model.MiddleName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.MiddleName, new { htmlAttributes = new { @class = "form-control", @maxlength = "50", @placeholder = "enter 'None' if none", @title = "Legal Middle Name" } })
@Html.ValidationMessageFor(model => model.MiddleName, "", new { @class = "text-danger" })
</div>
</div>

<div class="form-group">
@Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control", @maxlength = "50", @title = "Legal Last Name" } })
@Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" })


</div>
</div>



<div class="form-group" id="EmailGroup">
@Html.LabelFor(model => model.EmailAddress, htmlAttributes: new { @class = "control-label col-md-2", @id = "EmailLabel" })
<div class="col-md-10">
@Html.EditorFor(model => model.EmailAddress, new { htmlAttributes = new { @class = "form-control", @maxlength = "100", @title = "Email Address" } })
@Html.ValidationMessageFor(model => model.EmailAddress, "", new { @class = "text-danger" })


</div>
</div>

</div>





<div class="form-group">
<div class="col-md-2"></div>
<div class="col-md-8">
<input type="submit" value="Submit" id="affSubmit" class="btn btn-primary btn-md" />
</div>
</div>

</div>


}


@section Scripts {
@Scripts.Render("~/bundles/jqueryval")

@Scripts.Render("~/Scripts/Custom.js")



}


Layout.cshtml

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
</head>
<body>
<div class="navbar navbar-inverse navbar-default">

</div>
<div class="container body-content">
@RenderBody()
<hr />
</div>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@Scripts.Render("~/bundles/jquery-ui")
@RenderSection("scripts", required: false)





Custom.js

$().ready(function () {




$('form').submit(function (e) {

if ($('form').valid()) { //make sure form is valid

$('#affSubmit').prop('disabled', true);
$('#affSubmit').val("Please wait...");
this.submit();


}


}




});


ControllerAction

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(PersonModel model)
{

try
{

if (ModelState.IsValid)
{

PersonBLL bll = new PersonBLL();
PersonModel result = bll.SavePerson(model);

if (result.PersonID > 0)
{
return View("CreateSuccess");
}
else
{
ModelState.AddModelError("SaveError", "An error occurred while attempting to save, please try again later.");
}

}

}
catch (Exception ex) //unexpected error so log it, and redisplay form
{
Common.HandleException(ex);
ModelState.AddModelError("SaveError", "An error occurred while attempting to save, please try again later.");

}


//if we get here reshow page
return View(model);


}

lyz lyz
Answer

Use preventDefault when you submit with jQuery add this to your cutsom.js file

$().ready(function () {
   // not here
   $('form').submit(function (e) {
   e.preventDefault();// but here
   // the rest of your code here
   }
}

First I edited my answer, the e.preventDefault goes in the submit eventhandler. The default behaviour of a form is to be submitted when you hit the submit button so that's why your form gets submitted twice once because of the default behaviour and the second with your code this.submit(); e.preventDefault cancels the default behaviour of the submit event so the form gets submitted only with your code.