When performing post via ajax, Bad Request is returned instead of the JSON result

16,213

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.

Share:
16,213

Related videos on Youtube

ridermansb
Author by

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, 2022

Comments

  • ridermansb
    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
      Kerry Liu over 10 years
      If 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
      Lee Gary over 10 years
      can you show what's the value in data: ko.mapping.toJSON(data, map) on both IE and chrome?
    • ridermansb
      ridermansb over 10 years
      @KerryLiu Even setting processData: false, the error occurs. * About beforeSend, it was just a test. Even removed this event this error still occurs!
    • ridermansb
      ridermansb over 10 years
      @LeeGary The data: In IE "{"Id":"0"}" , In Chrome "{"Id":"0"}"
    • Brian North
      Brian North over 10 years
      Is it just me, or are your Chrome (response?) headers showing status code 400 Bad Request as well?
    • ridermansb
      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
    ridermansb over 10 years
    1. 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
    ridermansb over 10 years
    2. I am sending JSON to the server!
  • jemiloii
    jemiloii over 10 years
    well 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
    ridermansb over 10 years
    I use $.ajax because I set headers. how to set headers with $.post?
  • CaptainBli
    CaptainBli over 10 years
    I see. That is probably the only and best way if you are going to try to set your own headers.
  • CaptainBli
    CaptainBli over 10 years
    What is it in your headers that you need specifically?
  • CaptainBli
    CaptainBli over 10 years
    You 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&param2=54.... And this still let's you send the data.
  • Brian North
    Brian North over 10 years
    Seeing as the $.ajax method sends the exact same request with method: 'POST' as $.post does, and the sent/received headers in the OP do show that IE is sending it as a POST request, changing from the one method to the other will get the exact same results
  • CaptainBli
    CaptainBli over 10 years
    But it reduces the amount of extra work if you don't actually need it.