【问题标题】:How to send data using fetch and POST request with credentials included?如何使用包含凭据的 fetch 和 POST 请求发送数据?
【发布时间】: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"));,但邮递员仍然给我 * 而不是这个地址。

标签: javascript node.js reactjs cors fetch


【解决方案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的,结果是一样的
        猜你喜欢
        • 2020-11-24
        • 2019-03-28
        • 2019-10-12
        • 1970-01-01
        • 2022-12-16
        • 2017-12-17
        • 1970-01-01
        • 2014-06-16
        • 1970-01-01
        相关资源
        最近更新 更多