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?
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
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.
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.
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.
It's not your controller, that's working fine. You're missing a required field: both IE and Chrome are returning status code 400 Bad Request
- but only Chrome is properly processing the responseText
and giving you [{"Name":"Nome","ErrorMessage":"campo obrigatório."}]
meaning you have a missing form field.
Although I've searched all over and haven't found any reference to specific IE bugs in processing XMLHttpRequest.responseText
with non-200 status codes, it looks like IE is replacing your response body with its own:
Headers (Response)
HTTP/1.1 400 Bad Request
...
Content-Length: 11
Bad Request
Indicates that the "content" as it treats it is the "Bad Request" status text, not the proper json response (which Chrome reads as content-length 54, for instance). This might mean IE is discarding your response body (I doubt it, that'd be bloody incredible) or it just isn't be processed "properly." Try dumping the rest of your jqXHR
object and the arguments for your fail
handler to see if you can find it in there somewhere.
IE (all versions, including IE11) will put "Bad Request" in the status text and ignore the JSON you put in as the message.
In order to use the xhr.responseText in IE on error, you need to throw an exception instead of returning a Json or JsonResult with HttpStatusCode.BadRequest;
So... before:
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { Message = "There is already a distribution set which covers part or all of this period" });
This works in Chrome, FF and any sane browser, really. After:
throw new Exception("You have posted invalid datas.");
As an unhandled exception, it will be passed to the browser as a response, this will work in Chrome, FF and even in IE. It is not graceful, same as all unhandled exceptions (or just exceptions, for that matter), but it will do the job of letting you receive an appropriate response.
来源:https://stackoverflow.com/questions/22071211/when-performing-post-via-ajax-bad-request-is-returned-instead-of-the-json-resul