1

How could request headers be different for the same call and for the same JS code from the same browser?

I have one web API project and I am calling a method in this API from two different projects (CORS calls), using $http.get method of AngularJs 1.X.

$http.get("http://local.api:9002/api/default/get")
    .then(function success(response, status, headers, config) {
             console.info(response);
          },
          function error(response, status, headers, config) {
             console.info(response);
          }
         );

This is the same code that is being used from both front-end UIs to call the API.

However, for one the call succeeds and for the other it fails, stating below error.

Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Upon observation, I have found a difference in request headers for both the calls.

Requests Header for call that succeeds - AngularJS v1.6.4

Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-GB,en-US;q=0.8,en;q=0.6
Connection:keep-alive
Host:local.api:9002
Origin:http://local.ui.one:9001
Referer:http://local.ui.one:9001/
User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36

Response Header for the call that succeeds

Access-Control-Allow-Origin:http://local.ui.one:9001
Cache-Control:no-cache
Content-Length:19
Content-Type:application/json; charset=utf-8
Date:Fri, 05 May 2017 01:05:05 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/7.5
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET

Request Header for call that fails - AngularJS v1.2.17

Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-GB,en-US;q=0.8,en;q=0.6
Access-Control-Request-Headers:cache-control,if-modified-since,pragma,x-requested-with
Access-Control-Request-Method:GET
Connection:keep-alive
Host:local.api:9002
Origin:http://local.ui.two:9003
Referer:http://local.ui.two:9003/
User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36

Response header for call that fails

Cache-Control:no-cache
Content-Length:113
Content-Type:application/json; charset=utf-8
Date:Fri, 05 May 2017 01:22:14 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/7.5
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET

These are the two request headers that are additional in the second call.

Access-Control-Request-Headers:cache-control,if-modified-since,pragma,x-requested-with
Access-Control-Request-Method:GET

Note:

  1. My UI one and API are in the same Visual Studio Solution, while UI two is in a different Solution. I believe this could not be the reason but I could be wrong.
  2. I have different AngularJS versions in both the UI projects. Could this be a possible reason?

EDIT

As suggested by @Phil, I updated my backend API to handle the preflight requests coming from my 2nd UI project.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        //Some API routes here...

        var enableCorsAttribute = new EnableCorsAttribute(
            // Comma separated whitelist of allowed domains, notice no slash ('/') at the end of domain
            "http://local.ui.two:9003,http://local.ui.one:9001", 
            // Allowed Custom Headers
            "Origin, X-Requested-With, Content-Type, Accept, Authorization, Cache-Control, If-Modified-Since, Pragma",
            // Allowed methods
            "GET, PUT, POST, DELETE, OPTIONS"
        );

        config.EnableCors(enableCorsAttribute);
    }
}

And registered this WebApiConfig in Global.asax.

protected void Application_Start()
{
    GlobalConfiguration.Configure(WebApiConfig.Register);
}

And it started handling the calls from 2nd UI project as well, though the request headers were different.

Devraj Gadhavi
  • 3,541
  • 3
  • 38
  • 67
  • 2
    `I have different AngularJS versions in both the UI projects. Could this be a possible reason?` Yes. Don't do that :) – Phix May 05 '17 at 00:51
  • @Phix, thank you for the quick response. However, the second project is an existing project where upgrading the AngularJS version would not be cost effective. I could always downgrade the version in my first project though. I can give it a try at least. – Devraj Gadhavi May 05 '17 at 00:55
  • @Phix, I tried both upgrading the second project to Angular JS v1.6.4 and downgrading the first project to Angular JS v1.2.17. The result is the same. I believe it is not the Angular JS versions. – Devraj Gadhavi May 05 '17 at 01:07
  • In note point #1, what do you mean by *"solution"*? The error message is saying that your server isn't responding with the `Access-Control-Allow-Origin` response header – Phil May 05 '17 at 01:13
  • @Phil I meant, `Visual Studio Solutions`, where you can add multiple projects. – Devraj Gadhavi May 05 '17 at 01:15
  • Can you share the response headers for the two requests? – Phil May 05 '17 at 01:19
  • @Phil edited the question and added response headers. – Devraj Gadhavi May 05 '17 at 01:23
  • Whao, `Access-Control-Allow-Origin:http://local.ui.two:9001` for the request that **succeeds** (from `Origin:http://local.ui.one:9001`) doesn't make any sense – Phil May 05 '17 at 01:24
  • @Phil it was a typo, corrected! – Devraj Gadhavi May 05 '17 at 01:27
  • 3
    Why are you typing these? Please copy / paste – Phil May 05 '17 at 01:27
  • The rest all is copy pasted, I only edited the domains as I didn't want them to be public. – Devraj Gadhavi May 05 '17 at 01:29
  • Also, I believe the request headers for the non-working request are for the `OPTIONS` pre-flight. Can you verify and add the request methods for each set of headers to your question? – Phil May 05 '17 at 01:29
  • Both the requests methods are HTTP get methods. If that's what you meant. I have tried upgrading the AngularJS version for the call that fails but it gives the same result still fails. Also tried the other way around, downgrading AngularJS version for the call that succeeds and no issues there. So I believe it is not the AngularJS version that is causing the issue. – Devraj Gadhavi May 05 '17 at 01:39

2 Answers2

2

For call that fails - AngularJS v1.2.17

Access-Control-Request-Headers:cache-control,if-modified-since,pragma,x-requested-with
Access-Control-Request-Method:GET

This to me looks like the browser is doing a CORS pre-flight check to get permission to include non-standard headers. Look in the app to see why it is adding those non-standard headers.

Versions of AngularJS before v1.1.1 used to include x-request-with as a default and cause this problem.

Check for a config block that changes $httpProvider.defaults.

For the list of allowed headers that won't trigger a pre-flight request, see MDN HTTP Reference - Access_control_CORS (Simple Requests)

Community
  • 1
  • 1
georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • 1
    The bit that's still confusing me is that OP says they're using Angular 1.2.17 as the *older* version :/ – Phil May 05 '17 at 01:45
  • 2
    @Phil I agree. I think the app is adding `x-requested-with` and the other problematic headers. – georgeawg May 05 '17 at 01:48
  • @Phil and georgeawg Both you guys are geniuses! It was in fact, the app that was adding these headers in the configuration. Thank you for your help! – Devraj Gadhavi May 05 '17 at 01:53
  • 1
    It never occurred to me that there would be code adding this header on purpose. Nice deduction! – Phil May 05 '17 at 01:58
1

Here's my guess...

Older versions of Angular added the x-requested-with header. This has since been dropped. This header causes your CORS request to be non-simple which triggers a pre-flight OPTIONS request. Looks like your backend isn't set up to handle these requests.

You can work around this in the older Angular version by removing the header in your config function

delete $httpProvider.defaults.headers.common['X-Requested-With'];

The key was this line in what I assume is the request headers for the OPTIONS request

Access-Control-Request-Headers:cache-control,if-modified-since,pragma,x-requested-with


If you want your API to work correctly with cross-origin requests, it should respond correctly to pre-flight OPTIONS requests.

Phil
  • 157,677
  • 23
  • 242
  • 245
  • Both the requests methods are HTTP get methods. If that's what you meant. I have tried upgrading the AngularJS version for the call that fails but it gives the same result still fails. Also tried the other way around, downgrading AngularJS version for the call that succeeds and no issues there. So I believe it is not the AngularJS version that is causing the issue. – Devraj Gadhavi May 05 '17 at 01:40
  • But I will give this a try. – Devraj Gadhavi May 05 '17 at 01:40
  • @DevrajGadhavi Are you sure one of the requests isn't of type `OPTIONS`? Your browser's *Network* console should be able to tell you – Phil May 05 '17 at 01:41
  • 1
    Yeah, it is indeed `OPTIONS`. I was looking at the network tab and there it was showing get request. But the console shows OPTIONS. – Devraj Gadhavi May 05 '17 at 01:45
  • Let me try the configuration you are suggesting to remove the header. – Devraj Gadhavi May 05 '17 at 01:45
  • 1
    @DevrajGadhavi also make sure you don't have any code **adding** that header. I'd do a case-insensitive search of your code in UI #2 – Phil May 05 '17 at 01:48
  • Indeed, there was code that was adding the custom header like Angular 1.1. Removed it and it worked. Thank you for your help! – Devraj Gadhavi May 05 '17 at 01:53
  • @DevrajGadhavi the other answer guessed this correctly first. I strongly advise you to handle the pre-flight requests in your backend. As soon as you start posting JSON, you're going to run into the same problem – Phil May 05 '17 at 01:55
  • Sure I will add them back, I just needed to verify if my API was able to handle requests from more than one different domains, with a white list. – Devraj Gadhavi May 05 '17 at 01:57
  • Also awarded the accepted answer to the other answer, if that's what you meant. Cheers! – Devraj Gadhavi May 05 '17 at 01:59
  • 1
    @DevrajGadhavi it is indeed what I meant :) – Phil May 05 '17 at 02:00