3

jQuery 1.11.3 Post receives Bad Request from HttpResponseMessage but does not go into fail. Message from server is the following string:

"StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: , Headers: { }"

Should I not get an object back from HttpResponseMessage that says Bad Request? I'm using IIS Express.

Bad request

Back end:

[HttpPost]
public HttpResponseMessage DeleteOrderRow(int orderRowId)
{
    var row = OrderRowData.LoadItem(orderRowId);
    if (row == null)
    {
        AddAlert(AlertStyles.Danger, "Order row does not exist");
        //Below is the example being returned
        return new HttpResponseMessage(HttpStatusCode.BadRequest);
    }
    OrderRowData.Delete(orderRowId);
    AddAlert(AlertStyles.Success, "Order row has been removed");
    return new HttpResponseMessage(HttpStatusCode.OK);
}

jQuery:

$(document).on('click', '.delete-order-row', function (event) {
    event.preventDefault();

    var element = $(this);
    var id = element.data('id');

    if (id == null || id === -1) {
        element.closest('tr').remove();
    } else {
        var url = element.attr('href');
        $.post(url, { orderRowId: id })
            .done(function (data) {
                element.closest('tr').remove();
            })
            .fail(function (xhr, status, error) {
                location.reload();
            });
    }
});

Update: Checked network connection after tip from @smoksnes. The server actually sends 200 OK even though return new HttpResponseMessage(HttpStatusCode.BadRequest); is sent from back end. Is this normal behavior from IIS Express?

Network tab

Update 2: Solved it client side with this code. Based on @smoksnes reply and that IExceptionFilter and ExceptionFilterAttribute is not present in my project I'm suspecting Umbraco. Has anyone else experienced this in Umbraco?

.done(function (data) {
    if (data.indexOf("StatusCode: 400") !== -1) {
        $(window).scrollTop(0);
        location.reload();
    } else {
        element.closest('tr').remove();
    }
})
Ogglas
  • 62,132
  • 37
  • 328
  • 418
  • 1
    Check that the server actually returns 400 as the status code in the Network Tab. I've seen some implementations that return a 200, but with a JSON that contains another status code. – smoksnes Aug 02 '16 at 07:45
  • I did not think of that and right you are, the server returned 200 OK. Do you know if this is normal behavior from IIS Express? `new HttpResponseMessage(HttpStatusCode.BadRequest);` should clearly be 400 imao. – Ogglas Aug 02 '16 at 07:49

3 Answers3

1

Is this normal behavior from IIS Express?

In a blank project this should return 400 Bad Request.

400 Bad Request

public HttpResponseMessage DeleteOrderRow()
{
    return new HttpResponseMessage(HttpStatusCode.BadRequest);
}

But as I mentioned in the comments you seem to be getting a 200 OK, based on the information in the network tab. This is what's causing your client script to enter done(). This can be caused by custom filters.

Check for IExceptionFilter and ExceptionFilterAttribute in your project.

A common solution is adding these filters in Gobal.asax:

FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);


public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute()); // Or whatever filter you got.
        filters.Add(new AjaxExceptionLoggingFilter());
    }
}

They can change the response into a 200 OK.

Also, based on the url, you seem to be using Umbraco. I'm not very familiar with it, but there might be some magic going on there. If you cannot solve it on server side, you can solve it on client side:

   //Quick fix (dirty)
   $.post(url, { orderRowId: id })
        .done(function (data) {
            if(data.StatusCode != 200)
            {
                // do error stuff..
                return;
            }
            element.closest('tr').remove();
        })
        .fail(function (xhr, status, error) {
            location.reload();
        });

Or with ajaxPrefilter.

// Written by hand and not tested.
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
    var success = options.success;
    options.success = function(data, textStatus, jqXHR) {
        // override success handling
        if(data && data.StatusCode != 200)
        {
            // Go to error.
            return options.error(data);
        }
        else if(typeof(success) === "function") return success(data, textStatus, jqXHR);
    };
    var error = options.error;
    options.error = function(jqXHR, textStatus, errorThrown) {
        // override error handling
        if(typeof(error) === "function") return error(jqXHR, textStatus, errorThrown);
    };
});

Or with deferred:

$.ajaxPrefilter(function(opts, originalOpts, jqXHR) {
    // you could pass this option in on a "retry" so that it doesn't
    // get all recursive on you.
    if ( opts.retryAttempt ) {
        return;
    }

    var dfd = $.Deferred();

    // if the request works, return normally
    jqXHR.done(function(result){
        // Maybe check for result != null?
        if(result.StatusCode != 200) {
            dfd.reject() // Manually reject
        }
        else {
            dfd.resolve(result);
        }

    });

    jqXHR.fail(dfd.reject);

    // NOW override the jqXHR's promise functions with our deferred
    return dfd.promise(jqXHR);
});

A more complete example with ajaxPrefilter can be found here.

Community
  • 1
  • 1
smoksnes
  • 10,509
  • 4
  • 49
  • 74
  • Thanks for a clear answer. I can't find `IExceptionFilter` or `ExceptionFilterAttribute` in the project. I'm however using Umbraco which might be the problem. – Ogglas Aug 02 '16 at 08:12
  • 1
    Thanks @smoksnes, already made a client side fix. Unfortunately the response is not even an object so I have to check a string for "StatusCode: 400". You can view how I did it in my question. – Ogglas Aug 02 '16 at 08:31
  • 1
    @Ogglas, a very weird solution by Umbraco IMO. And if you want a more universal solution you can use ajaxPrefilter. Then all your requests will behave "as normal" again. – smoksnes Aug 02 '16 at 08:45
0

I think that the problem is that you are returning a HttpResponseMessage every time, you should throw a response exception instead:

var resp = new HttpResponseMessage(HttpStatusCode.BadRequest);
throw new HttpResponseException(resp);
Eyescream
  • 902
  • 5
  • 14
  • This does not work 100%, it goes into fail but it is because the server responds with a HTTP 500 Internal Server Error message. – Ogglas Aug 04 '16 at 11:50
  • try by modifying the return type of your from your DeleteOrderRow function to IHttpActionResult then return BadRequest("Order row does not exist"); (or for success Ok()); – Eyescream Aug 04 '16 at 15:11
0

Solved it client side with this code. Based on @smoksnes reply and that IExceptionFilter and ExceptionFilterAttribute is not present in my project I'm suspecting Umbraco.

.done(function (data) {
    if (data.indexOf("StatusCode: 400") !== -1) {
        $(window).scrollTop(0);
        location.reload();
    } else {
        element.closest('tr').remove();
    }
})
Ogglas
  • 62,132
  • 37
  • 328
  • 418