10

My c#/WebApi server code looks like:

[HttpPost]
public HttpResponseMessage Logout()
{
    // do some stuff here ... 
    return Request.CreateResponse(HttpStatusCode.OK);
}

In the client I do an ajax call using jquery 2.0.3

ADDED: Jquery code (typescript)...

    var ajaxSettings: JQueryAjaxSettings = {};
    ajaxSettings.type = 'POST';
    ajaxSettings.data = null;
    ajaxSettings.contentType = "application/json; charset=utf-8";
    ajaxSettings.dataType = "json";
    ajaxSettings.processData = false;

    ajaxSettings.success = (data: any, textStatus: string, jqXHR: JQueryXHR) => {
        console.log("Success: textStatus:" + textStatus + ", status= " + jqXHR.status);
    };
    ajaxSettings.error = (jqXHR: JQueryXHR, textStatus: string, errorThrow: string) => {
        console.log("Error: textStatus:" + textStatus + ", errorThrow = " + errorThrow);
    };

    $.ajax("http://apidev.someurl.com/v1/users/logout", ajaxSettings);

ADDED 2: Request headers resulting from the above code:

POST http://apidev.someurl.com/v1/users/logout HTTP/1.1
Host: apidev.someurl.com
Connection: keep-alive
Content-Length: 0
Cache-Control: no-cache
Pragma: no-cache
Origin: http://apidev.someurl.com
Authorization: SimpleToken 74D06A21-540A-4F31-A9D4-8F2387313998
X-Requested-With: XMLHttpRequest
Content-Type: application/json; charset=utf-8
Accept: application/json, text/javascript, */*; q=0.01
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
Referer: http://apidev.someurl.com/test/runner/apitest/index.html?
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8

The response is 200, yet the error handler from the ajax call is fired instead of the success handler. Reason: parseerror, unexpected end of input.

One solution is to change serverside code to:

return Request.CreateResponse<String>(HttpStatusCode.OK, "Logout ok");

I understand that an empty response is not valid JSON, but the response message is intentionally empty. The 200 says it all. The response headers look like:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/7.5
Access-Control-Allow-Origin: http://apidev.someurl.com
Access-Control-Allow-Credentials: true
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Sun, 05 Jan 2014 01:20:30 GMT
Content-Length: 0

Is this a bug in jquery? Or should the Request.CreateResponse(OK) never be used that way? Should I fix this with some workaround in the client? AFAIK the server is not doing it wrong here ... any thoughts?

EDIT: Thanks to feedback of kevin, nick and John this issue has become clear. The solution I choose is to return a NoContent

return Request.CreateResponse(HttpStatusCode.NoContent);

Serverside this seems for this case the correct code. Clientside this is handled perfectly by JQuery (the succes handler is called). Thanx all for clearing this up!

(I don't know who to give credit for the answer ... since nick and kevin gave their valuable feedback in comments, johns feedback also added a better understanding) ... if no other suggestions are given ... i'll later mark the only 'answer' as the Answer)

Thanks all!

Paul0515
  • 17,167
  • 9
  • 28
  • 45
  • If you're ajax is setup to expect json, and *"the response message is intentionally empty."*, then that's why it's going to error. empty response is an error if the response is expected to be json. – Kevin B Jan 05 '14 at 01:34
  • If you are wanting to intentionally leave the reponse empty, then you could instead return `HttpStatusCode.NoContent` - it will still be treated as a success, but jQuery shouldn't have problems with the empty response. – nick_w Jan 05 '14 at 01:42
  • I added the client code. Humm... I do specify datatype="json" ... What if in other cases than the 200, the result contains more info which the client prefers to get in json.... The response headers don't specify the datatype of json. I'll insert the request headers as well... – Paul0515 Jan 05 '14 at 01:56
  • *"Is this a bug in jquery"* No, it's intended. Empty string isn't valid json, thats why it enters error. If you want to use json, you should make all responses return json, even if it's just `{}` – Kevin B Jan 05 '14 at 02:00
  • the change to this way of looking at it was made to make $.parseJSON more inline with how JSON.parse works, that way both modern browsers and older browsers will work the same, regardless of whether or not $.parseJSON direcly mapped to the native method. – Kevin B Jan 05 '14 at 02:03
  • kevin, would jquery not do it slightly more correct when taking into account the response header content-length=0, or is this header simply not meant for that purpose? – Paul0515 Jan 05 '14 at 02:15
  • nick_w ... I'd like to respond with an OK, and not provide any additional content, that is more info than saying NoContent. – Paul0515 Jan 05 '14 at 02:17
  • `NoContent` still means that the request was successful. From [MSDN](http://msdn.microsoft.com/en-us/library/system.net.httpstatuscode(v=vs.110).aspx), `NoContent indicates that the request has been successfully processed and that the response is intentionally blank`. and `OK indicates that the request succeeded and that the requested information is in the response`. – nick_w Jan 05 '14 at 02:25
  • @Paul0515 I see where you coming from, but which one happened first? you specifying that you want json, or the server returning plain text? You can modify the way jquery handles this pretty easily to allow the text->json->object conversion to allow the text to be empty string if you wanted to, resulting in pre-1.9 functionality. Yes, it likely could do more looking at the content type, but it likely wont. – Kevin B Jan 05 '14 at 02:26
  • Here's another way of looking at it. If you set the dataType to JSON, and the server returns JSON with the text/html content type(which is very common), what should jquery do? honor the text/html content type and break a lot of code, or parse it as json? – Kevin B Jan 05 '14 at 02:28
  • nick_w ... ahh indeed. I did not realize that. You're right. Testing now! – Paul0515 Jan 05 '14 at 02:31
  • 204 seems to work. I edited the question to include the findings. Thanks!! – Paul0515 Jan 05 '14 at 03:30
  • In that case do you mind if a post an answer covering the 204 vs 200 situation? – nick_w Jan 05 '14 at 03:36
  • @SalmanA Any particular reason you marked this as a duplicate? The two questions are really quite different. – nick_w Jul 24 '15 at 09:09
  • @nick_w OP states _The response is 200, yet the error handler from the ajax call is fired instead of the success handler_. The reason why error handler is fired is explained in that answer. OP chose another approach to solve the issue, this does not make the question not-a-duplicate. – Salman A Jul 24 '15 at 10:44
  • @SalmanA But the substance of the two questions is actually quite different. In this question the OP was simply returning an incorrect status code given the response. In the duplicate question an incorrect response was returned given the data type requested. Do you agree? – nick_w Jul 24 '15 at 10:52
  • @nick_w Sorry, I respectfully disagree and believe that OP should be returning `{}` or `""` instead of experimenting with alternate status codes. Let reopen votes decide if it is a duplicate or not. – Salman A Jul 24 '15 at 11:07
  • @SalmanA Take a look at my answer to this question. It is clear that an appropriate action to take in this case is to return 204 (as there is no desire to return content but the request still succeeded). I can't see how doing this could be deemed "experimenting with alternate status codes" - the status code in question is intended for this kind of situation. Do you have any references or links that support your position that a blank response should be returned in this case instead of a 204? – nick_w Jul 24 '15 at 11:32

2 Answers2

18

From MSDN, HttpStatusCode.OK is defined as follows (emphasis mine):

Equivalent to HTTP status 200. OK indicates that the request succeeded and that the requested information is in the response. This is the most common status code to receive.

Because a 200 implies some content is sent with the response (even an empty JSON object literal would be fine), you can run into problems if jQuery's Ajax implementation assumes a non-zero length response but does not receive it, especially if it tries parsing JSON (and possibly XML) from it. This is why John S makes the suggestion of changing the dataType to text; doing so would allow you to take specific action when receving an empty response.

On the other hand, HttpStatusCode.NoContent is defined as (emphasis mine):

Equivalent to HTTP status 204. NoContent indicates that the request has been successfully processed and that the response is intentionally blank.

In your particular situation, it may make more sense to set the status code to HttpStatusCode.NoContent to ensure that jQuery Ajax understands that it does not need to attempt any parsing/processing of the response.

nick_w
  • 13,990
  • 2
  • 47
  • 67
5

This is how jQuery works.

JQuery will assume the response is JSON if either:

  1. You specify dataType: 'json' in the ajax call, or
  2. You do not include the dataType setting but jQuery detects the response is JSON because of a response header.

And when jQuery assumes the response is JSON, it automatically tries to parse the response into objects before it calls the success handler. If the parse fails, the error handler is called instead.

From the jQuery documentation:

As of jQuery 1.9, an empty response is also rejected; the server should return a response of null or {} instead.

I don't know why jQuery couldn't make an exception for an empty response and treat it the same as null, or even as undefined, but if you cannot (or won't) change the response, a work-around would be to specify dataType: 'text' and then parse the response yourself.

$.ajax(url, {
    type: 'post',
    dataType: 'text',
    data: params,
    success: function(text) {
        if (text) {
            var data = jQuery.parseJSON(text);
            // ...
        }
    }
});
John S
  • 20,117
  • 7
  • 39
  • 52
  • John, thanx ... "jquery assumes the response is json when the call includes dataType : json ..." Is that ok? Does JQuery not have to look at the response headers received from the server? It could see that no datatype is specified there AND that the Content-length is set to 0. In the original request it specifies 'accept' header with json but also wildcards for any type. – Paul0515 Jan 05 '14 at 02:10
  • @Paul0515 I think that's fine. If you set dataType: json, an dthe server returns XML with the XML content type, that should be an error because XML isn't JSON. Otherwise, what's the point of even setting the datatype property? I think in your case, you would like the functionality that occurs without a specified datatype, where everything is handled by the contentType returned. – Kevin B Jan 05 '14 at 02:12
  • @Paul0515 - My understanding is that the `dataType` setting trumps whatever is specified in the response content-type header. As KevinB points out, you could leave off the `dataType` setting and specify a different content type for an empty response. I think it might be odd though to have a success handler where the first parameter is sometimes an object and sometimes an empty string, but I think it would work. I personally always specify the `dataType` setting (but I try to specify the correct content-type too). – John S Jan 05 '14 at 02:27