我试图从惠普Alm的REST API中获取一些数据。它使用一个小的curl脚本工作得很好——我得到了我的数据。

现在用JavaScript、fetch和ES6(或多或少)来实现这一点似乎是一个更大的问题。我一直收到这个错误信息:

无法加载获取API。对飞行前请求的响应则不然 pass访问控制检查:没有' access - control - allow - origin '头 显示在所请求的资源上。“http://127.0.0.1:3000”是 因此不允许访问。响应的HTTP状态代码为501。 如果不透明响应满足您的需求,请将请求的模式设置为 'no-cors'获取禁用CORS的资源。

我明白这是因为我试图从我的本地主机内获取数据,解决方案应该使用跨起源资源共享(CORS)。我认为我确实这样做了,但不知为何,它要么忽略了我在头文件中写的内容,要么是其他问题。

那么,是否存在执行问题?我做错了吗?很遗憾,我无法查看服务器日志。我真的有点卡在这里了。

function performSignIn() {

  let headers = new Headers();

  headers.append('Content-Type', 'application/json');
  headers.append('Accept', 'application/json');

  headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
  headers.append('Access-Control-Allow-Credentials', 'true');

  headers.append('GET', 'POST', 'OPTIONS');

  headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));

  fetch(sign_in, {
      //mode: 'no-cors',
      credentials: 'include',
      method: 'POST',
      headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}

我正在使用Chrome浏览器。我也尝试使用Chrome CORS插件,但随后我得到另一个错误消息:

响应中的'Access-Control-Allow-Origin'报头的值 当请求的凭据模式为时,一定不能是通配符'*' “包括”。因此,来源“http://127.0.0.1:3000”是不允许的 访问。对象发起的请求的凭据模式 XMLHttpRequest由withCredentials属性控制。


当前回答

我犯过很多次这样的错误,正因为如此,我给大家列了一张“清单”。

Enable CORS on your project: If you're using Node.js (by example) you can use: npm install cors; import cors from 'cors'; app.use(cors()); You can manually set the headers like this (if you want it): app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authortization'); res.setHeader('Acces-Control-Allow-Methods', 'GET, POST, PATCH, DELETE'); Remember to add http:// to your API link in your frontend project, some browsers like Chrome do not accept a request using CORS if the request URL isn't HTTP or HTTPS: http://localhost:3000/api Check if your project is using a proxy.config.js file. See Fixing CORS errors with Angular CLI proxy.

其他回答

如果你在macOS的开发环境中使用Node.js和Express.js作为后端,React和Axios作为前端,你需要在HTTPS下运行两边。以下是最终对我有效的方法(经过几个小时的深入研究和测试):

步骤1:创建SSL证书

只需按照如何在5分钟内让HTTPS在本地开发环境中工作的步骤进行操作。

你最终会得到几个文件作为凭据来运行HTTPS服务器和React web:

server.key & server.crt

您需要在前端和后端的根文件夹中复制它们(在生产环境中,您可能会考虑在后端的./ssh文件夹中复制它们)。

步骤2:后端设置

我读了很多建议使用“cors”包甚至设置(“Access-Control-Allow-Origin”,“*”)的回答,这就像在说:“欢迎黑客来我的网站”。就像这样做:

import express from 'express';
const emailRouter = require('./routes/email');  // in my case, I was sending an email through a form in React
const fs = require('fs');
const https = require('https');

const app = express();
const port = 8000;

// CORS (Cross-Origin Resource Sharing) headers to support Cross-site HTTP requests
app.all('*', (req, res, next) => {
    res.header("Access-Control-Allow-Origin", "https://localhost:3000");
    next();
});

// Routes definition
app.use('/email', emailRouter);

// HTTPS server
const credentials = {
  key: fs.readFileSync('server.key'),
  cert: fs.readFileSync('server.crt')
};

const httpsServer = https.createServer(credentials, app);
httpsServer.listen(port, () => {
    console.log(`Back-end running on port ${port}`);
});

如果你想测试https是否正确,你可以用下面的常量替换httpserver:

https.createServer(credentials, (req: any, res: any) => {
  res.writeHead(200);
  res.end("hello world from SSL\n");
}).listen(port, () => {
  console.log(`HTTPS server listening on port ${port}...`);
});

然后从web浏览器https://localhost:8000/访问它

步骤3:前端设置

这是来自React前端的Axios请求:

    await axios.get(`https://localhost:8000/email/send`, {
        params: { /* Whatever data you want to send */ },
        headers: {
            'Content-Type': 'application/json',
        }
    })

现在,您需要使用我们已经创建的SSL凭据以HTTPS模式启动React web。在macOS终端中输入以下内容:

HTTPS=true SSL_CRT_FILE=server.crt SSL_KEY_FILE=server.key npm start

此时,您正在从前端端口3000的HTTPS连接发送请求,后端端口8000的HTTPS连接将接收该请求。CORS应该对此感到高兴;)

这个答案涵盖了很多领域,所以它分为三个部分:

如何使用CORS代理来避免“无访问控制-允许起源头”问题 如何避免飞行前的CORS 如何修复“Access-Control-Allow-Origin头必须不是通配符”的问题


如何使用CORS代理来避免“无访问控制-允许起源头”问题

如果您不控制前端代码发送请求的服务器,并且来自该服务器的响应的问题只是缺乏必要的Access-Control-Allow-Origin报头,那么您仍然可以通过CORS代理发出请求来使事情正常工作。

您可以使用https://github.com/Rob--W/cors-anywhere/中的代码轻松运行自己的代理。 您还可以轻松地部署自己的代理到Heroku,只需2-3分钟,只需5个命令:

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

运行这些命令后,您将拥有自己的CORS Anywhere服务器,例如,https://cryptic-headland-94862.herokuapp.com/。

现在,用代理的URL作为请求URL的前缀:

https://cryptic-headland-94862.herokuapp.com/https://example.com

添加代理URL作为前缀会导致请求通过您的代理发出,这是:

将请求转发到https://example.com。 接收来自https://example.com的响应。 向响应添加Access-Control-Allow-Origin标头。 将该响应以及添加的报头传递回请求前端代码。

然后浏览器允许前端代码访问响应,因为带有access - control - allow - origin响应头的响应是浏览器所看到的。

即使请求是触发浏览器执行CORS preflight OPTIONS请求的请求,这也可以工作,因为在这种情况下,代理还会发送Access-Control-Allow-Headers和Access-Control-Allow-Methods报头,以使preflight成功。


如何避免飞行前的CORS

问题中的代码触发了CORS预飞行——因为它发送了一个授权报头。

https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

即使没有,Content-Type: application/json头也会触发preflight。

“preflight”的意思是:在浏览器尝试问题中的代码中的POST之前,它首先向服务器发送一个OPTIONS请求,以确定服务器是否选择接收一个具有授权和Content-Type: application/json头的跨源POST。

它用一个小的curl脚本工作得很好-我得到了我的数据。

为了正确地测试curl,你必须模拟浏览器发送的preflight OPTIONS:

curl -i -X OPTIONS -H "Origin: http://127.0.0.1:3000" \
    -H 'Access-Control-Request-Method: POST' \
    -H 'Access-Control-Request-Headers: Content-Type, Authorization' \
    "https://the.sign_in.url"

将https://the.sign_in.url替换为实际的sign_in URL。

浏览器从OPTIONS请求中需要的响应必须有这样的头:

Access-Control-Allow-Origin:  http://127.0.0.1:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization

如果OPTIONS响应不包括这些头,浏览器将立即停止,并且永远不会尝试发送POST请求。另外,响应的HTTP状态代码必须是2xx—通常是200或204。如果是其他状态码,浏览器就会立即停止。

问题中的服务器用501状态代码响应OPTIONS请求,这显然意味着它试图表明它不支持OPTIONS请求。在这种情况下,其他服务器通常使用405“Method not allowed”状态代码进行响应。

因此,如果服务器对OPTIONS请求的响应是405或501,而不是200或204,或者没有响应必要的响应头,那么您将永远无法从前端JavaScript代码直接向该服务器发出POST请求。

避免在问题中触发预飞行的方法是:

如果服务器不需要授权请求头,而是依赖于POST请求体中嵌入的身份验证数据或作为查询参数 如果服务器不要求POST体具有Content-Type: application/json媒体类型,而是接受POST体为application/x-www-form-urlencoded,带有一个名为json(或其他什么)的参数,其值为json数据


如何修复“Access-Control-Allow-Origin头必须不是通配符”的问题

我得到另一个错误消息: 响应中的'Access-Control-Allow-Origin'报头的值 当请求的凭据模式为时,一定不能是通配符'*' “包括”。因此,来源“http://127.0.0.1:3000”是不允许的 访问。对象发起的请求的凭据模式 XMLHttpRequest由withCredentials属性控制。

对于具有凭据的请求,如果access - control - allow - origin报头的值为*,浏览器将不允许前端JavaScript代码访问响应。相反,这种情况下的值必须完全匹配前端代码的起源http://127.0.0.1:3000。

请参阅MDN HTTP访问控制(CORS)文章中的凭据请求和通配符。

如果你控制你要发送请求的服务器,处理这种情况的常见方法是配置服务器以获取Origin请求头的值,并将其回显/反射回Access-Control-Allow-Origin响应头的值;例如,使用nginx:

add_header Access-Control-Allow-Origin $http_origin

但这只是一个例子;其他(web)服务器系统也有类似的方法来回显原始值。


我正在使用Chrome浏览器。我也尝试使用Chrome CORS插件

Chrome CORS插件显然只是简单地在浏览器看到的响应中注入了一个Access-Control-Allow-Origin: *头。如果这个插件再聪明一点,它所做的就是将这个伪Access-Control-Allow-Origin响应头的值设置为前端JavaScript代码的实际原点http://127.0.0.1:3000。

所以避免使用该插件,即使是用于测试。只是为了分散注意力。要在没有浏览器过滤的情况下测试从服务器得到的响应,最好使用curl -H。


至于问题中fetch(…)请求的前端JavaScript代码:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

去掉这些线条。Access-Control-Allow-*头是响应头。你绝对不想在请求中发送它们。这样做的唯一效果是触发浏览器进行预飞行。

对于Node.js,如果你正在使用路由器,请确保在路由器之前添加CORS。否则,仍然会得到CORS错误。像下图:

const cors = require('cors');

const userRouter = require('./routers/user');

expressApp = express();
expressApp.use(cors());
expressApp.use(express.json());
expressApp.use(userRouter);

当客户端URL和服务器URL(包括端口号)不匹配时,会发生此错误。在这种情况下,您需要启用跨源资源共享的CORS服务。

如果您正在托管Spring REST服务,那么您可以在Spring框架中的CORS支持的博客文章中找到它。

如果你使用Node.js服务器托管服务,那么

停止Node.js服务器。 NPM安装cors—保存 将以下行添加到server.js中

const cors=require("cors");
const corsOptions ={
   origin:'*', 
   credentials:true,            //access-control-allow-credentials:true
   optionSuccessStatus:200,
}

app.use(cors(corsOptions)) // Use this after the variable declaration

当客户端从他的主机username.companyname.com调用我们的后端服务时,他常常得到上述错误

有两件事是必须的:

在发送回响应时,发送键为Access-Control-Allow-Origin且值为*的报头: context.Writer.Header()["Access-Control-Allow-Origin"] = []string{"*"} //避免CORS错误 使用Go CORS库将AllowCredentials设置为false, AllowAllOrigins设置为true。