Mike Bailey
Mike Bailey

Reputation: 12817

AJAX request fails when headers are set but succeeds without them

I have an ASP.NET Web API application running on my local instance of IIS. The web application is configured with CORS. The Web API method I am calling reads something like:

[POST("/API/{foo}/{bar}")]
public String DoSomething(String foo, String bar)
{
    return "Hello, World!";
}

Client side, I'm running the following request:

$.ajax({
    url: "http://machine/API/Foo/Bar",
    type: "POST",
    success: function (data) {
        console.log("Success: " + data);
    },
    error: function (xhr, status, message) {
        console.log("Failure: " + status + ", " + message);
    }
});

The corresponding console log within Firefox 15.0.1 reads as:

test.html (line 38)
POST http://machine/API/Foo/Bar

200 OK
        112ms   
jquery-1.7.2.js (line 8240)

Success: "Hello, World!"

This works flawlessly when I'm accessing my local IIS server as well as when I have it accessing the corresponding URL for the IIS Express development instance within Visual Studio.

When I add a single request header, anything at all, the following request fails when made against my local IIS server:

$.ajax({
    url: "http://machine/API/Foo/Bar",
    type: "POST",
            onBeforeSend: function (xhr, settings) {
                xhr.setRequestHeader("A", "B");
            },
    success: function (data) {
        console.log("Success: " + data);
    },
    error: function (xhr, status, message) {
        console.log("Failure: " + status + ", " + message);
    }
});

The ever so verbose log message I see is:

Failure: error, 

The same POST request is successful when made against the IIS Express development instance within Visual Studio.

In Fiddler, the previous two requests go through without failure regardless of whether I'm targeting the full IIS server or IIS Express within Visual Studio:

// Request
POST http://machine/Foo/Bar HTTP/1.1
Host: machine
Content-Length: 0

// Response
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 38
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 09 Oct 2012 20:16:29 GMT

"Hello, World!"

The permutations {With Headers, Without Headers} x {Full IIS, IIS Express} all produce identical output within Fiddler.

In short: Why does having request headers set cause the AJAX call to fail against my local fully fledged IIS instance?

Furthermore, why does my requests succeed in Fiddler but not in my Javascript code? This is a serious deal breaker if I can't set request headers in a proper IIS instance.

Upvotes: 2

Views: 1900

Answers (2)

Mike Bailey
Mike Bailey

Reputation: 12817

The actual underlying issue with my CORS problem was related to the pipeline utilized by IIS 7.

When IIS 7 is operating under the Classic Pipeline, requests were not being preflighted with the OPTIONS request. They would fail silently as I described in my question. I saw this in the Net tab of Firebug and via interactive debugging with the Thinktecture IdentityModel library explicitly included as a project to my solution.

But when IIS 7 operates under Integrated Pipeline, CORS is respected. POST requests and requests with custom headers are correctly preflighted.

Upvotes: 0

Trinima
Trinima

Reputation: 19

This might have something to do with cross origin resource sharing. At a recent project, we ran into something similar, and cross origin resource sharing with custom http headers turned out to be the problem.

This should only be occurring if the domain of the javascript is different than the domain of the ajax call being made.

To determine if this is the case, you can try putting something similar to this in your Global.asax:

protected void Application_BeginRequest(object sender, EventArgs e)
{
    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");

    if (HttpContext.Current.Request.HttpMethod == "OPTIONS" )
    {
        //These headers will handle the HTTP OPTIONS call sent by the browser
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods" , "GET, POST" );
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "A, Foo, Bar");
        HttpContext.Current.Response.End();
    }
}

CAUTION Be wary of doing something like this. Setting the Access-Control-Allow-Origin header to a value of "*" is a security risk.

Additional Material: http://www.w3.org/TR/cors/

Upvotes: 1

Related Questions