CORS Header 仅在 Angular 资源请求中缺失

CORS Header Missing on Angular Resource Requests Only

我在本地主机上有一个可用的 node/express 后端 运行。我正在构建一个需要从 goodreads api 获取数据的项目应用程序。当我执行请求时,我得到:

Cross-Origin Request Blocked: 
The Same Origin Policy disallows reading the remote resource at 
https://www.goodreads.com/book/title.json?author=Arthur+Conan+Doyle&key=[my_key]&title=Hound+of+the+Baskervilles. 
(Reason: CORS header 'Access-Control-Allow-Origin' missing).1 <unknown>

服务器端,一切正常。我启用了 CORS,当我检查 headers 时,在检查 Firefox 中的 header 和 Chrome 开发工具后,'Access-Control-Allow-Origin' 可用于来自我服务器的所有内容。但是,当我通过 $resource 发出请求时,'Allow-Access...' 不在我的 header 中。这是资源的代码:

.factory('goodReads', function($resource) {
    return $resource('https://www.goodreads.com/book/title.json');
})
.controller('AddBookSelectorController', function($resource, goodReads) {
    this.fetch = function() {
        var key = '[my_key]';
        var data = goodReads.query({author: 'Arthur Conan Doyle', key: key, title: 'Hound of the Baskervilles'});
        console.log(data);
    };
});

我正在通过 ng-click 调用 fetch,除我收到 CORS 错误外,一切执行正常。谁能指出我正确的方向?我是 angular 的新手,我怀疑我的资源请求或配置中存在问题,但我似乎无法在文档或其他 Whosebug 问题中找到解决我的问题的答案。

更新 3: 这不是本地主机问题。我尝试将它推送到我的域并使用一个简单的按钮 运行 对 OpenBooks api 的 xhr 请求,但问题变得更糟了。它通过 Openshift 托管,现在 'Allow-Control-Access-x' header 甚至连我服务器上的其他文件都消失了。真的开始用头撞墙了。我正在删除 Angular 标签,因为它与 Angular.

无关

更新 2: 我在 Chrome 中安装了 'Allow-Control-Allow-Origin' 扩展后就可以正常工作了。我的问题是我在本地主机上 运行 吗?还是有其他事情发生?如果没有扩展名,header 仍未设置。

更新: 我从早上 8 点就开始研究这个,但仍然没有成功。我已经尝试使用 Angular 的 $http 和 Javascript 的 xhr 重写请求,遵循 HTML5 Rocks | Using Cors 的示例,但我仍然对每种方法遇到相同的问题。就像我说的,必要的 header 信息可以从我服务器上的文件中获得,但是当我向其他站点发出请求时它会中断。

我开始认为这可能不是 Angular 问题,但我真的不知道。为了安全起见,这是我添加到 Express 中以启用 CORS 的代码,包括 app.use 这样您就可以了解我在哪里调用它:

app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, Content-Length");
    res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
    res.header("Access-Control-Allow-Credentials", "true");
    next();
});
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);

编辑:这是来自 API 请求的 header:

请求Headers

Host: www.goodreads.com
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101     Firefox/40.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://localhost:3000/
Origin: http://localhost:3000
Connection: keep-alive

回应Headers

Cache-Control: max-age=0, private, must-revalidate
Content-Encoding: gzip
Content-Length: 686
Content-Type: application/json; charset=utf-8
Date: Wed, 02 Sep 2015 17:20:35 GMT
Etag: "a2be782f32638d2a435bbeaf4b01274a-gzip"
Server: Server
Set-Cookie: csid=BAhJIhg1MzgtNTk4NjMzNy0wNzQ4MTM5BjoGRVQ%3D--afed14b563e5a6eb7b3fa9005de3010474230702; path=/; expires=Sun, 02 Sep 2035 17:20:33 -0000
locale=en; path=/
_session_id2=fd45336b8ef86010d46c7d73adb5f004; path=/; expires=Wed, 02 Sep 2015 23:20:35 -0000; HttpOnly
Status: 200 OK
Vary: Accept-Encoding,User-Agent
X-Content-Type-Options: nosniff, nosniff
X-Frame-Options: ALLOWALL
X-Request-Id: 1K8EJWG30GWDE4MZ4R5K
X-Runtime: 2.277972
X-XSS-Protection: 1; mode=block

Headers 来自我服务器的 .js 文件:

要求

Host: localhost:3000
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101   Firefox/40.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://localhost:3000/
Cookie: _ga=GA1.1.1924088292.1439681064; connect.sid=s%3AB4O0Up9WF5iqkfky__I0XCiBD2aMATlq.gbJUC9GseqnJvRTEIbcwxD6cwFQeL7ljNScURCJ5As0
Connection: keep-alive
If-Modified-Since: Wed, 02 Sep 2015 17:08:40 GMT
If-None-Match: W/"886-14f8f0828c1"
Cache-Control: max-age=0

回复:

Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Cache-Control: public, max-age=0
Connection: keep-alive
Date: Wed, 02 Sep 2015 17:20:30 GMT
Etag: W/"886-14f8f0828c1"
Last-Modified: Wed, 02 Sep 2015 17:08:40 GMT
X-Powered-By: Express
access-control-allow-headers: Origin, X-Requested-With, Content-Type, Accept

我想这个问题暴露了我的无知,但也许这会对像我这样的其他 COR 新手有所帮助。在获得 CORs in Action 的副本并使用 Flickr API 完成第一个示例后,我终于解决了问题。

我的问题与后端、Angular、jQuery 的 .ajax 方法或 xhr 无关。我所有的请求都格式正确。 问题是我尝试使用的 APIs 没有在他们的服务器上启用 COR。 O.o 一旦我将数据类型更改为 jsonp,一切经历过。

无论如何,对于像我这样的新手,如果您 运行 遇到这个问题,这里有一些可以帮助您的建议:

1.不要假设您正在使用的 API 启用了 CORs

我不知道为什么,但我盲目地选择了两个没有启用 COR 的 API,这就是让我大惊小怪的原因。我以前从未 运行 遇到过这个问题,因为我使用 API 所做的工作一直来自像 Flickr 这样启用了 COR 的大公司。如果他们没有在他们的服务器上设置 Access-Control-Allow-Origin,您可以请求他们启用它并同时使用 JSONP。

如果 API 最后有一个回调选项,这是一个好兆头,您应该为您的请求使用 JSONP。 JSONP 的工作原理是将您的请求包装在回调中并利用脚本标记的功能。脚本可以从任何域中提取其他脚本,因此它可以作为获取数据的黑客。这是一个很好的 link 帮助了我。 Exactly What is JSONP? | CameronSpear.com

2。检查响应 Headers

我被这个骗了,但请记住,您对外部 API 请求的响应 header 是来自 他们的 服务器的响应,而不是你的。你的服务器是否启用了 CORs 并不重要,你是在向其他人发出请求,浏览器会在请求中自动将你的信息发送给他们header。请记住,出于安全原因,所有这些检查都是由浏览器完成的,因此它会根据您的 ajax 调用在请求端为您完成繁重的工作。如果 Access-Control-Whatever 没有出现在响应 header 中,则它们没有启用 COR。如果你在前端工作并请求别人的数据,你无能为力。使用 JSONP,你的问题就会消失(可能)。

对我来说,这整个惨败的开始是因为我混淆了来自我的服务器的响应和来自他们服务器的响应。我在自己的服务器上正确启用了 COR,但我认为它没有将原始信息附加到请求 header,这就是响应 header 中没有它的原因。实际上,一切正常,但 API 服务器没有启用它。

虽然度过了一天,但学到了很多东西。希望我浪费的时间可以帮助其他人解决他们的 COR 问题。请注意,我的问题与堆栈无关,因此无论您如何提出请求,如果您 运行 遇到 COR 问题,检查响应 header 是第一个采取的行动。之后,我建议查看请求本身是否有错误。

查看上面那本书或同一作者的这本 link 以获得更多帮助,尤其是在 non-simple 请求 HTML5 Rocks | Using CORs.

方面