仅在 Angular 资源请求中缺少 CORS 标头

Posted

技术标签:

【中文标题】仅在 Angular 资源请求中缺少 CORS 标头【英文标题】:CORS Header Missing on Angular Resource Requests Only 【发布时间】:2015-11-28 08:19:10 【问题描述】:

我有一个在本地主机上运行的工作节点/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,当我检查标头时,在检查 Firefox 和 Chrome 开发工具中的标头后,来自我服务器的所有内容都可以使用“Access-Control-Allow-Origin”。但是,当我通过 $resource 发出请求时,我的标头中不存在“允许访问...”。这是资源的代码:

.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 新手,我怀疑我的资源请求或配置中有问题,但我似乎无法在文档或其他 *** 问题中找到解决问题的答案。

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

更新 2: 在 Chrome 中安装“Allow-Control-Allow-Origin”扩展程序后,我得到了它。我的问题是我在本地主机上运行它吗?还是有其他事情发生?如果没有扩展,标题仍然没有设置。

更新:我从早上 8 点开始就一直在做这个,但仍然没有运气。我尝试使用 Angular 的 $http 和 javascript 的 xhr 重写请求,遵循html5 Rocks | Using Cors 的示例,但每种方法仍然存在相同的问题。就像我说的,必要的标头信息可以从我服务器上的文件中获得,但是当我向其他站点发出请求时它会中断。

我开始认为这可能不是 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 请求的标头:

请求标头

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

响应标头

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

来自我的服务器的 .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

【问题讨论】:

您可以发布您的 API 请求的标头吗? 感谢您的关注,弗雷德。我根据您的要求添加了标题信息。 【参考方案1】:

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

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

无论如何,对于像我这样的新手,如果遇到此问题,这里有一些提示可以帮助您:

1.不要假设您使用的 API 已启用 CORs

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

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

2。检查响应标头

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

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

就这样度过了一天,但学到了很多教训。希望我浪费的时间可以帮助其他人解决他们的 COR 问题。请注意,我的问题与堆栈无关,因此无论您如何提出请求,如果遇到 COR 问题,检查响应标头是首先要采取的行动。在那之后,我建议调查请求本身是否有错误。

查看上面的那本书或同一作者的此链接以获得更多帮助,尤其是在涉及非简单请求时HTML5 Rocks | Using CORs。

【讨论】:

以上是关于仅在 Angular 资源请求中缺少 CORS 标头的主要内容,如果未能解决你的问题,请参考以下文章

仅在特定条件下被 CORS 策略阻止的 Angular 请求

跨域请求被阻止:同源策略不允许读取远程资源,CORS 标头中缺少令牌“缓存控制”

CORS HEADERS 仅在预检或每个请求中出现

CORS HEADERS 仅在预检或每个请求中出现

Angular 应用程序仅在 chrome 中出现 CORS 错误

在 Angular 2 中使用基本身份验证预检 CORS 请求