When performing post via ajax, Bad Request is returned instead of the JSON result
Solution 1
Think this is an issue with IIS trying to use custom error response rather sending the error message that the controller is generating.
<system.webServer>
...
<httpErrors existingResponse="PassThrough"></httpErrors>
...
</system.webServer>
Or
Response.TrySkipIisCustomErrors = true;
Reference - https://stackoverflow.com/a/4029197/1304559
Check out this blog post http://weblog.west-wind.com/posts/2009/Apr/29/IIS-7-Error-Pages-taking-over-500-Errors
Since response code is set to 400, IIS replaces your content with its custom error page content
Solution 2
Your response comes back with Content-Type: text/html
http header, but it should be application/json
. This is not an issue in Chrome (and you don't get mismatch warnings in the console) because you're relying on overrideMimeType
... which, you guessed it, is not IE-friendly. In fact, the following is never executed on older versions of IE:
if (x && x.overrideMimeType) {
return x.overrideMimeType("application/json;charset=UTF-8");
}
Your solution could be to make sure the content is served with correct content type. If you're familiar with tampering tools like Burp Suite, you could add the correct header on-the-fly and see if that fixes the problem. I would probably avoid inlining methods like AddHeader
and see if there is a way this can be fixed at a higher - routing, perhaps - level.
Solution 3
The issue I am seeing is that your trying to set the encoding of the JSON to UTF-8. Normally it works just fine, however, on IIS, it has a custom error to report that the UTF-8 is not necessary.
Few other things to note. You are doing a POST, so the server will be expecting a json file. If provided none, it will not know what to do.
Solution 4
I have filled in a fail test that should help.
$.post($frm.attr("action"), ko.mapping.toJSON(data, map))
.done(function (dataVal) {
//process dataVal
var mystruct = GenerateCache($.parseJSON(dataVal));
})
.fail(function (jqxhr, textStatus, error) {
if (jqxhr.responseText.indexOf("YourMoniker") != -1) {
parseData($.parseJSON(jqxhr.responseText));
} else {
var err = textStatus + ', ' + error;
console.log("Request Failed: " + err);
}
});
function GenerateCache(data) {
var obj = function () { };
obj.prototype = data;
return new obj();
}
Specifically look at the error handling in the .fail
section.
Related videos on Youtube
ridermansb
I’ve been in the information technology area for over 17 years, working in and out of Brazil. Throughout my journey, I’ve developed software that communicated with automated robots, created applications and web pages, worked in emerging startups, and specialised myself in front-end technologies. I'm curious, enthusiastic and student most of the time, like the rest of the time to write code, especially in Javascript.
Updated on June 04, 2022Comments
-
ridermansb about 2 years
Javascript
jqXHR = $.ajax({ url: $frm.attr("action"), type: "POST", dataType: "json", cache: false, headers: headers, contentType: "application/json;charset=UTF-8", data: ko.mapping.toJSON(data, map), beforeSend: function(x) { if (x && x.overrideMimeType) { return x.overrideMimeType("application/json;charset=UTF-8"); } } }); jqXHR.fail(function(xhr, err, msg) { /* xhr.responseText NEED TO BE JSON!!! */ });
In Chrome
Headers
Request Method:POST Status Code:400 Bad Request Request Headersview source Accept:application/json, text/javascript, */*; q=0.01 Accept-Encoding:gzip,deflate,sdch Accept-Language:en-US,en;q=0.8,pt-BR;q=0.6,pt;q=0.4 Connection:keep-alive Content-Length:10 Content-Type:application/json;charset=UTF-8 User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36 X-Requested-With:XMLHttpRequest Request Payloadview source {Id:0} Response Headersview source Cache-Control:private Content-Length:54 Content-Type:application/json; charset=utf-8 Date:Thu, 27 Feb 2014 14:01:59 GMT Server:Microsoft-IIS/8.0 X-AspNet-Version:4.0.30319 X-AspNetMvc-Version:5.1 X-Powered-By:ASP.NET
Response
[{"Name":"Nome","ErrorMessage":"campo obrigatório."}]
Works in chrome!
In IE8
Headers (Request)
POST /Motivos/Salvar HTTP/1.1 Accept: application/json, text/javascript, */*; q=0.01 Accept-Language: pt-br x-requested-with: XMLHttpRequest Content-Type: application/json;charset=UTF-8 Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0) Content-Length: 10 Connection: Keep-Alive Pragma: no-cache
Headers (Response)
HTTP/1.1 400 Bad Request Cache-Control: private Content-Type: text/html Server: Microsoft-IIS/8.0 X-AspNetMvc-Version: 5.1 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET Date: Thu, 27 Feb 2014 13:51:46 GMT Content-Length: 11 Bad Request
NOT WORK!!
Asp.net MVC
Filter
public class HandleExceptionAttribute : HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception != null) { filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; var ex = filterContext.Exception.GetBaseException(); filterContext.Result = new JsonResult { JsonRequestBehavior = JsonRequestBehavior.AllowGet, Data = new { ex.Message, ex.GetType().Name } }; filterContext.ExceptionHandled = true; } else { base.OnException(filterContext); } } }
Apply on GlobalFilterCollection
Controller
[ValidateJsonAntiForgeryToken, HttpPost] public virtual JsonResult Salvar(TViewModel viewModel) { if (ModelState.IsValid) { TEntity model; if (default(TKey).Equals(viewModel.Id)) { model = Mapper.Map<TEntity>(viewModel); AdicionarEntidade(model, viewModel); } else { model = Repositorio.Get(viewModel.Id); Mapper.Map(viewModel, model, typeof(TViewModel), typeof(TEntity)); SalvarEntidade(model, viewModel); } return SalvarResult(model); } Response.StatusCode = 400; return Json(ModelState.ToJson(), JsonRequestBehavior.AllowGet); }
Extenssion
public static object ToJson(this ModelStateDictionary dic, params string[] othersMessages) { var states = (from e in dic where e.Value.Errors.Count > 0 select new { Name = e.Key, e.Value.Errors[0].ErrorMessage }).ToList(); if (othersMessages != null) foreach (var message in othersMessages) states.Add(new { Name = "", ErrorMessage = message }); return states; }
Questions
- Why not have the xhr.resposeText object?
- How to retrieve JSON in the same way that I recover in Chrome?
I need the JSON to populate the form!
Notes: 03/11/2014
When I add
Response.TrySkipIisCustomErrors = true;
in my controler, it works! responseText returns the json. Why?-
Kerry Liu over 10 yearsIf you set a different content-type in $.ajax it'd be wise to set processData to false. Also, why are you overriding mime type in beforeSend? You have control over the return type on the server.
-
Lee Gary over 10 yearscan you show what's the value in data: ko.mapping.toJSON(data, map) on both IE and chrome?
-
ridermansb over 10 years@KerryLiu Even setting
processData: false
, the error occurs. * AboutbeforeSend
, it was just a test. Even removed this event this error still occurs! -
ridermansb over 10 years@LeeGary The data: In IE
"{"Id":"0"}"
, In Chrome"{"Id":"0"}"
-
Brian North over 10 yearsIs it just me, or are your Chrome (response?) headers showing status code
400 Bad Request
as well? -
ridermansb over 10 years@BrianNorth Is correct. ha that a required field is not filled, the status is correct in Chrome. responseText is is wrong in IE
-
ridermansb over 10 years1. About UTF-8 does not make sense, because it works in Chrome. I've tried removing the UTF-8 and the error still occurs!
-
ridermansb over 10 years2. I am sending JSON to the server!
-
jemiloii over 10 yearswell what is the json that you are sending? You only show the response json, response is what the server sends the client. ALso, you can only send one json file at a time. So if you have multiple forms being submited at once, it needs to be packed as one json file.
-
ridermansb over 10 yearsI use
$.ajax
because I set headers. how to set headers with$.post
? -
CaptainBli over 10 yearsI see. That is probably the only and best way if you are going to try to set your own headers.
-
CaptainBli over 10 yearsWhat is it in your headers that you need specifically?
-
CaptainBli over 10 yearsYou can send the dataType like
$.post( url [, data ] [, success(data, textStatus, jqXHR) ] [, dataType ] )
. You can also add data to the query string in the url like:/getData.aspx?param1=test¶m2=54...
. And this still let's you send the data. -
Brian North over 10 yearsSeeing as the
$.ajax
method sends the exact same request withmethod: 'POST'
as$.post
does, and the sent/received headers in the OP do show that IE is sending it as aPOST
request, changing from the one method to the other will get the exact same results -
CaptainBli over 10 yearsBut it reduces the amount of extra work if you don't actually need it.