如何使用包含凭据的 fetch 和 POST 请求发送数据?

Posted

技术标签:

【中文标题】如何使用包含凭据的 fetch 和 POST 请求发送数据?【英文标题】:How to send data using fetch and POST request with credentials included? 【发布时间】:2019-09-02 04:02:46 【问题描述】:

我想使用 fetch 从端口 3000 上的 React 前端以 JSON 格式将一些数据发送到 3005 上的 node.js 服务器。我在服务器上配置了 cors,但每次尝试使用 cookie 发送请求时, Chrome 抛出错误:

从源“http://localhost:3000”获取“http://localhost:3005/user-connected”的访问权限已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:“Access-Control-Allow-Origin”的值' 当请求的凭据模式为 'include' 时,响应中的标头不能是通配符 '*'。

所有来自服务器代码的 console.log 都被跳过。

当我在获取代码中删除标题时

“内容类型”、“应用程序/json”

我得到了 cookie,但没有数据。包含此标头但没有凭据:“include”,我可以获取我的数据,但我永远不会同时获取两者。

这是我的获取代码:

fetch("http://localhost:3005/user-connected", 
            mode: "cors",
            method: "post",
            headers: [
                ["Content-Type", "application/json"],
            ],
            credentials: "include",
            body: JSON.stringify(data: "123")
        )
            .then(data => data.json())
            .then((data) => 
               console.log(`Response: $JSON.stringify(data)`);
            ).catch(err => 
                console.log(`Error: $err`)
        );

Node.js cors 配置:

app.use(cors(credentials: true));

app.use((req, res, next) => 
  res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
  res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,Content-Type', 'Access-Control-Allow-Origin', 'Origin');
  res.setHeader('Access-Control-Allow-Credentials', true);
  next();
);

还有我的发帖路线:

let cookiesData = (req, res) => 
  console.log(`Cookie user-connected: $req.cookies.io`)


  console.log(`Received data: $JSON.stringify(req.body)`);
  res.send( status: "OK" );


router.post("/user-connected", cors(origin: 'http://localhost:3000'), cookiesData);

甚至可以做我想做的事吗?或者我错过了一些重要的配置?

【问题讨论】:

@Mark 我做了,但没有任何改变。是的,它是节点 您能否向您的服务器发送一个带有 OPTIONS 的请求(飞行前请求),以验证 cors 标头是否设置正确? @JonasWilms 我不知道该怎么做。当我尝试使用 fetch 执行此操作时,我收到错误,它不是有效的方法。当我使用 POST 获取时,服务器告诉我 OPTION 请求以代码 204 结束 使用 Postman 或 curl (getpostman.com) @JonasWilms 当我向 Postman 提出 OPTIONS 请求时,我得到 Access-Control-Allow-Origin -> * 所以如果我想要凭证不是很好吗?我将此行添加到我的服务器 app.options('*', cors("localhost:3000"));,但邮递员仍然给我 * 而不是这个地址。 【参考方案1】:

添加 Content-Type 将在发送实际请求之前首先触发预检请求。预检请求方法是 OPTIONS。 OPTIONS 在请求标头中不包含 cookie(至少对于 Chrome 和 Firefox)。

错误可能是因为这段代码:

console.log(`Cookie user-connected: $req.cookies.io`)

req.cookies 在预检请求期间为空,当您尝试获取 req.cookies.io 时会抛出错误。预检请求要求服务器返回 200(请参阅https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests)。您的代码有错误可能会抛出 500,从而向您显示可怕的 CORS 错误消息。

您必须先处理预检请求。

在服务器端,尝试处理 CORS 代码中的 OPTIONS 方法以在 next() 之前返回 200:

app.use((req, res, next) => 
  res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
  res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,Content-Type', 'Access-Control-Allow-Origin', 'Origin');
  res.setHeader('Access-Control-Allow-Credentials', true);

  if (req.method === 'OPTIONS') 
    res.sendStatus(200);
    return;
  

  next();
);

这将阻止任何代码 next() 在预检请求期间执行,并告诉客户端可以发送实际请求。

【讨论】:

【参考方案2】:

您需要在您的 fetch 请求中为 post 方法传递这样的标头:


  credentials: 'include',
  mode: 'cors',
  method: 'POST',
  headers:  'Content-Type': 'application/json' ,

您必须确保您已授予 localhost:3000 的访问权限允许来源

【讨论】:

【参考方案3】:

当前端和后端的连接过程中出现错误时,通常最好将问题分离到这些端,所以首先让我们完全忽略前端。相反,我们可以使用 curl 或Postman 查询后端,通过它您可以了解服务器实际返回的内容。

现在,在您的情况下,CORS 飞行前请求是最重要的,可以通过向特定服务器路径发送 OPTIONS 请求来检查它是否设置正确。

然后你会发现Wildcards and credentials can't be used at the same time。

现在,如果您更改路由本身的 cors( ... ) 选项,您会发现这不会影响返回的实际 OPTIONS 标头。也就是说,因为第一个 cors() 将终止所有 OPTION 请求并会使用 uts 配置进行响应,所以所有进一步的 cors() 中间件都不会改变任何东西。

因此,您应该或者添加一个全局 cors 处理程序:

  app.use(cors(origin: 'http://localhost:3000', credentials: true ));
 // don't use cors() again!

或者(这可能更好,因为它允许细粒度的访问控制)将 cors 设置单独添加到每个路由:

 app.use("/route", cors(origin: 'http://localhost:3000'));
 app.get("/route", (req, res) =>  /*...*/ );

您也可以将其添加到路由器,以便 cors 策略仅影响特定路径。

【讨论】:

【参考方案4】:

尝试使用标题作为对象:

headers: 
    "Content-Type": "application/json",
,

文档Fetch Reference - Headers 显示了带有对象而非数组的示例

【讨论】:

之前我是这样设置headers的,结果是一样的

以上是关于如何使用包含凭据的 fetch 和 POST 请求发送数据?的主要内容,如果未能解决你的问题,请参考以下文章

无法从包含凭据的 URL 构造请求

如何使用 GraphQL POST 请求进行授权获取?

React JS - 如何通过 fetch 语句验证凭据

fetch跨域发送带凭据的请求

在 C# WebAPI 中,如何配置 CORS 以允许带有凭据和 json 内容的 POST 请求?

fetch跨域问题