为什么同一个 ajax 调用有不同的请求 headers?

Why there are different request headers for same ajax call?

对于同一调用和来自同一浏览器的同一 JS 代码,请求 header 怎么可能不同?

我有一个 Web API 项目,我正在从两个不同的项目 (CORS calls) 调用这个 API 中的一个方法,使用 $http.get 方法 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);
          }
         );

这与 front-end UI 中用于调用 API 的代码相同。

但是,一个调用成功,另一个调用失败,并显示以下错误。

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

经过观察,我发现这两个调用的请求 header 存在差异。

请求 Header 调用成功 - 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

响应成功的调用Header

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

请求 Header 失败的调用 - 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

响应失败的调用header

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

这是第二次调用中附加的两个请求 header。

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

注:

  1. 我的UI一个和API在同一个Visual Studio Solution,而UI两个在不同的Solution。我相信这不是原因,但我可能是错的。
  2. 我在两个 UI 项目中有不同的 AngularJS 版本。这可能是一个可能的原因吗?

编辑

按照@Phil 的建议,我更新了我的后端 API 以处理来自我的第二个 UI 项目的预检请求。

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);
    }
}

并在 Global.asax 中注册了这个 WebApiConfig。

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

它也开始处理来自第二个 UI 项目的调用,尽管请求 header 不同。

这是我的猜测...

旧版本的 Angular 添加了 x-requested-with header。这已经被删除了。此 header 导致您的 CORS 请求成为 non-simple,从而触发 pre-flight OPTIONS 请求。看起来您的后端未设置为处理这些请求。

您可以在旧 Angular 版本中通过删除 config 函数中的 header 来解决此问题

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

关键是这一行,我假设是 OPTIONS 请求

的请求 header

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


如果您希望 API 正确处理 cross-origin 请求,它应该正确响应 pre-flight OPTIONS 请求。

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

在我看来,浏览器正在执行 CORS pre-flight check 以获得包含 non-standard headers 的权限。在应用程序中查看为什么要添加 non-standard headers.

v1.1.1 之前的 AngularJS 版本默认包含 x-request-with 并导致此问题。

检查更改 $httpProvider.defaults 的配置块。

有关不会触发 pre-flight 请求的允许 headers 的列表,请参阅 MDN HTTP Reference - Access_control_CORS (Simple Requests)