First off, sorry about the vague title but this is an issue which has become nothing short of a nightmare for me over the past few days. From time to time I will encounter this issue but I have never found any SO threads or questions posted anywhere resembling my issue and how to address it.
I'm using an Ajax.BeginForm
in my view (with no partial views). Upon submitting the form, the action method on my controller will be hit fine, and code runs until the end before returning a HttpStatusCodeResult
.
The action will then be hit repeatedly immediately again (with no consistency of how many times), before the Ajax call stops (it shows ABORTED in Firebug) and the client receives a blank error with the error code being 'error'.
Inspecting the net tab of firebug gives me virtually nothing to go on, and the $(document).ajaxError()
global handler also gives me no useful information.
I have access to IISExpress trace file logs, but I cannot decipher any useful information (if someone could point me to a location that would be fantastic).
I have updated (via NuGet) the jQuery, unobstrusive ajax, validate, unobtrusive validate packages and the issue persists. There are references to jQuery and the others only once on my page (so no duplicate event handlers).
This is an issue which has happened using MVC 4 and 5, and also across both Firefox and Chrome. Chrome dev tools reveals similar information (nothing useful).
I have no event handlers attached to the form in my Javascript. The only submitting is being done by the Ajax.BeginForm.
For reference, my view:
@using Project.Models
@model LoginInfo
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Login</title>
<script src="~/Scripts/jquery-2.2.0.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<script src="/Scripts/jquery.validate.min.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script src="~/Content/Scripts/login.js"></script>
<script>
$('html').removeClass('nojs');
</script>
</head>
<body>
<div id="container">
@using (Ajax.BeginForm("Ajax", "Login", new AjaxOptions { OnBegin = "submit", OnComplete = "response" }))
{
<img id="logo" src="~/Content/Images/Main.png" alt="" />
<table>
<tr>
<td>
@Html.LabelFor(x => x.serverHost)
</td>
<td>
@Html.TextBoxFor(x => x.serverHost, new { placeholder = "domain", required = "required" })
</td>
</tr>
<tr>
<td>
@Html.LabelFor(x => x.instanceName)
</td>
<td>
@Html.TextBoxFor(x => x.instanceName, new { placeholder = "company", required = "required" })
</td>
</tr>
<tr>
<td>
@Html.LabelFor(x => x.username)
</td>
<td>
@Html.TextBoxFor(x => x.username, new { placeholder = "Username", required = "required" })
</td>
</tr>
<tr>
<td>
@Html.LabelFor(x => x.password)
</td>
<td>
@Html.PasswordFor(x => x.password, new { placeholder = "Password", required = "required" })
</td>
</tr>
</table>
<table>
<tr>
<td id="remembertd">
@Html.LabelFor(x => x.remember, "Remember me")
@Html.CheckBoxFor(x => x.remember, new { @checked = Model.remember })
</td>
<td>
<input id="submit" type="submit" value="Connect" />
</td>
</tr>
</table>
}
</div>
</body>
</html>
My controller / action:
[AllowAnonymous]
public class LoginController : Controller
{
public ActionResult Index()
{
return View(new LoginInfo());
}
[ValidateInput(true)]
public ActionResult Ajax(LoginInfo info)
{
HttpStatusCodeResult result = new HttpStatusCodeResult(200);
if (!ModelState.IsValid)
{
result = GetFailedResult(new ModelStateInvalidException(), Reason.ModelStateInvalid);
}
else
{
try
{
var i = 0;
var j = 10 / i;
}
catch (Exception ex)
{
result = GetFailedResult(ex, Reason.Unhandled);
}
}
return new HttpStatusCodeResult(result.StatusCode, result.StatusDescription);
}
private HttpStatusCodeResult GetFailedResult(Exception ex, Reason reason)
{
HttpStatusCode httpErrorCode = HttpStatusCode.InternalServerError;
String errorMessage = String.Empty;
//Trace
RemotingHelper.Trace("Login attempt failed with message: " + ex.Message + "\n----------");
//Get message/code
switch (reason)
{
case Reason.IncorrectInstanceName:
case Reason.IncorrectServerHost:
case Reason.IncorrectServerHostPort:
case Reason.IncorrectUsernameOrPassword:
httpErrorCode = HttpStatusCode.Forbidden;
errorMessage = "Could not connect using the provided details.";
break;
case Reason.Offline:
httpErrorCode = HttpStatusCode.ServiceUnavailable;
errorMessage = "Could not establish connection with the server.";
break;
case Reason.LicenseMissing:
httpErrorCode = HttpStatusCode.InternalServerError;
errorMessage = "There was a problem with your license.\nPlease contact an administrator.";
break;
default:
httpErrorCode = HttpStatusCode.InternalServerError;
errorMessage = "There was an error attempting to log in.\nPlease contact an administrator.";
break;
}
//Return
return new HttpStatusCodeResult(httpErrorCode, errorMessage + ":" + ((Int32)reason).ToString());
}
}
Relevant models:
public enum Reason
{
Network = 10,
Offline = 11,
LicenseMissing = 12,
LicenseFileMissing,
NullChannel = 14,
ModelStateInvalid = 15,
Unhandled = 16,
IncorrectServerHost = 17,
IncorrectServerHostPort = 18,
IncorrectInstanceName = 19,
IncorrectUsernameOrPassword = 20
}
public class LoginInfo
{
public LoginInfo()
{
serverHost = string.Empty;
instanceName = string.Empty;
username = string.Empty;
password = string.Empty;
remotingPassword = null;
remember = false;
}
[Required]
[Display(Name = "Server Host")]
public string serverHost { get; set; }
[Required]
[Display(Name = "Instance Name")]
public string instanceName { get; set; }
[Required]
[Display(Name = "Username")]
public string username { get; set; }
[Required]
[Display(Name = "Password")]
public string password { get; set; }
[Display(Name = "Remoting Password")]
public string remotingPassword { get; set; }
[Display(Name = "Persistent")]
public bool remember { get; set; }
}
Is there any not-so-obvious limit I may be breaking? (Like HttpStatusCodeResult.StatusDescription
has a max limit of 512 chars - note: this is not happening in my example).
Hopefully somebody can shed some light on this issue, like I mentioned it's been horrible to troubleshoot and I'm getting nowhere. Any help would be incredibly appreciated. Thank you.
Update (11/02/16): JS response function (failure and success are simply animation functions):
function response(data) {
setTimeout(function () {
if (data.status == 200) {
success();
}
else {
var info = data.statusText.split('|');
var message = info[0];
var code = info[1];
console.log("Login failed with error code: " + code);
failure(message);
}
}, 1000);
}
Update (15/02/16): Installed Fiddler, and error doesn't occur anymore with Fiddler running (I'm guessing it intercepts the empty response and returns with a code of it's own?). Although Fiddler is reporting the following:
HTTP/1.1 504 Fiddler - Receive Failure
Date: Mon, 15 Feb 2016 12:36:05 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Cache-Control: no-cache, must-revalidate
Timestamp: 12:36:05.548
[Fiddler] ReadResponse() failed: The server did not return a complete response for this request. Server returned 0 bytes.
As a temporary work-around I am returning a JSONResult from the action. Not a fix, but a workaround until I find something more revealing on the internet (no luck so far after ~7 days of searching.)