【问题标题】:How to fix 'TypeError: Failed to fetch'?如何修复“TypeError:无法获取”?
【发布时间】:2019-11-15 10:33:46
【问题描述】:

当我尝试在前端使用 fetch 并在后端使用快速路由发送发布请求时,我收到了 TypeError: Failed to fetch 错误。

我能够在数据库中成功创建新用户,但是当尝试通过 fetch promise 获取新用户数据时,就会抛出错误。

app.js

function createNewUser() {
  let formUsername = document.getElementById('signup-username').value;
  let formEmail = document.getElementById('signup-email').value;
  let formPassword = document.getElementById('signup-password').value;
  let url = "/users";
  let newUserData = {
    username: formUsername,
    email: formEmail,
    password: formPassword
  }

  fetch(url, {
    method: 'POST',
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, *same-origin, omit
    headers: {
        'Content-Type': 'application/json'
    },
    redirect: 'follow', // manual, *follow, error
    referrer: 'no-referrer',
    body: JSON.stringify(newUserData),
  }).then(res => res.json())
  .then(response => console.log('Success: ', JSON.stringify(response)))
  .catch(error => console.error('Error: ', error));
}

users.js

router.post('/users', function(req, res) {
   User.create(req.body)
   .then(function(user) {
      res.json({
         user: user
      })
   }
});

server.js

const express = require('express');
const app = express();
const fs = require('fs');
const path = require('path');
const bodyParser = require('body-parser');
const bcrypt = require('bcryptjs');
const auth = require('./auth');
const router = require('./routes/routes.js');

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(router);

app.use('/', express.static(path.join(__dirname, 'public')));

app.use((req, res, next) => {
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader(
    "Access-Control-Allow-Methods",
    "OPTIONS, GET, POST, PUT, PATCH, DELETE" // what matters here is that OPTIONS is present
  );
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization", "Access-Control-Allow-Origin");
  next();
});

app.listen(3000, function(){
  console.log("Listening on port 3000");
});

我需要取回该用户对象才能访问其数据。

编辑: 所以,我发现问题与如何在前端提交请求有关。如果我创建以下函数,然后在加载 app.js 时调用它,那么一切正常:

function createNewUserTest() {
  let formUsername = 'dd';
  let formEmail = 'd@d.com';
  let formPassword = 'secrete';
  let url = "/api/users";
  let newUserData = {
    username: formUsername,
    email: formEmail,
    password: formPassword
  }
  fetch(url, {
    method: 'POST',
    cache: 'no-cache',
    credentials: 'same-origin',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(newUserData),
  })
  .then(res => res.json())
  .then(response => console.log('Success: ', response))
  .catch(error => console.error('Error: ', error));
}

createNewUserTest();

但是,如果我尝试通过表单中的onsubmit 或html 中按钮上的onclick 调用此函数,或者如果我使用事件侦听器(见下文,位于app.js 中),然后我得到TypeError: Failed to fetch 错误:

let signupSubmitButton = document.getElementById('signup-submit');
signupSubmitButton.addEventListener('click', createNewUserTest);

这对我来说更加莫名其妙。我需要使用Vanilla JS,我需要通过表单提交来创建用户,但不确定我需要在这里调整什么。

解决方案 再次被event.preventDefault() 挫败。这就是我所需要的。

let signupForm = document.getElementById('signup-form');
signupForm.addEventListener('submit', function(event) {
  event.preventDefault();
  let formUsername = document.getElementById('signup-username').value;
  let formEmail = document.getElementById('signup-email').value;
  let formPassword = document.getElementById('signup-password').value;
  let url = "/api/users";
  let newUserData = {
    username: formUsername,
    email: formEmail,
    password: formPassword
  }
  fetch(url, {
    method: 'POST',
    cache: 'no-cache',
    credentials: 'same-origin',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(newUserData),
  })
  .then(res => res.json())
  .then(response => console.log('Success: ', response))
  .catch(error => console.error('Error: ', error));
});

【问题讨论】:

  • 同源请求不需要 CORS...我想这就是它抛出的原因,考虑到您的服务器不会使用 CORS 标头响应。
  • @PatrickRoberts 感谢您的回复!尝试删除 mode: 'cors',但我仍然遇到同样的错误。
  • "Access-Control-Origin",正确命名为 Access-Control-Allow-Origin,是一个 response 标头,应该从服务器发送到客户,而不是相反。
  • @Bergi 我将server.js 代码添加到我的初始请求中。我还删除了 CORS 属性,并从我的 headers 对象中删除了 "Access-Control-Origin",但仍然出现相同的错误。
  • 嗯,更新后的代码看起来应该可以工作了。您要在哪个域上运行它? devtools 对网络请求有什么看法?

标签: javascript node.js express fetch


【解决方案1】:

问题是关于“TypeError failed to fetch”。正如其他答案所探讨的那样,消息的措辞向网络/服务器/CORS 类型问题的方向发送一个,但我发现有一个原因是完全不同的。

我遇到了这个问题,并以表面的价值看待它一段时间,特别是困惑,因为它是由我的页面在 Chrome 中而不是在 Firefox 中发布引起的。

直到我发现 chrome://net-internals/#events 并看到我的请求遭受 'delegate_blocked_by = "Opening Files"' 之后,我才终于有了线索。

我的请求是通过文件输入元素发布从用户计算机上传的文件。该文件恰好是在 Excel 中打开的文件。虽然它在 Firefox 中可以正常发布,但只有在关闭时才能在 Chrome 中发布。

您的网络应用程序的用户需要被告知这个潜在问题,网络开发人员还应该注意“TypeError failed to fetch”有时可能意味着“TypeError 没有达到尝试获取的程度”

【讨论】:

    【解决方案2】:

    问题是页面正在重新加载,这使我无法及时获取数据。解决方案是在侦听器中简单地添加event.preventDefault()。

    app.js

    let signupForm = document.getElementById('signup-form');
    signupForm.addEventListener('submit', function(event) {
      event.preventDefault();
      let formUsername = document.getElementById('signup-username').value;
      let formEmail = document.getElementById('signup-email').value;
      let formPassword = document.getElementById('signup-password').value;
      let url = "/api/users";
      let newUserData = {
        username: formUsername,
        email: formEmail,
        password: formPassword
      }
      fetch(url, {
        method: 'POST',
        cache: 'no-cache',
        credentials: 'same-origin',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(newUserData),
      })
      .then(res => res.json())
      .then(response => console.log('Success: ', response))
      .catch(error => console.error('Error: ', error));
    });
    

    【讨论】:

    • 这正是我需要的!
    • 一个很难确定的错误!谢谢!!
    【解决方案3】:

    当涉及到 CORS 问题时,通常是因为服务器不知道如何正确处理它。基本上,每次您在请求中包含像 access-control-origin 这样的标头时,它也会发起 OPTIONS 预检请求,并且如果您的服务器不期望它会抛出错误,因为它只期望 POST 请求。

    换句话说 - 在没有 "Access-Control-Origin": "*" 部分的情况下再试一次,看看它是否有效,或者只是尝试在服务器上使用类似这样的东西修补它:

    app.use((req, res, next) => {
      res.setHeader("Access-Control-Allow-Origin", "*");
      res.setHeader(
        "Access-Control-Allow-Methods",
        "OPTIONS, GET, POST, PUT, PATCH, DELETE" // what matters here is that OPTIONS is present
      );
      res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
      next();
    });
    

    【讨论】:

    • 我将此块添加到我的 server.js 文件(我正在运行 node server.js),甚至将 "Access-Control-Allow-Origin 添加到最后一个 res.setHeader 方法,但仍然得到相同的错误.让我想知道我的 server.js 文件是否有问题,所以我将其添加到我的初始请求中。
    • 现在我看到你已经用 server.js 文件更新了你的代码,我想我知道出了什么问题。当您设置标头时,您已经在部件之前初始化了所有路由 (app.use(router))。在 Node 中,请求从文件的顶部到底部进行,因此即使您添加了 setHeaders 部分,也为时已晚,因为请求已经通过了路由,而服务器上没有设置任何标头。设置标题后尝试移动路线,看看是否有效。
    • 也试过了,但没有运气。我认为后端的响应一定有一些东西。我在上面的 cmets 中注意到了这一点,但是我能够对类似设置的控制器执行成功的 GET 请求,我什至能够通过 Postman 从 POST 请求中获得成功的响应。 Firefox 显示 304 Not Modified 错误。我对此进行了一些研究,但我不确定为什么这就是我所得到的。也只是尝试清除缓存,但也没有这样做。
    猜你喜欢
    • 2018-03-22
    • 2021-10-03
    • 2021-10-28
    • 1970-01-01
    • 1970-01-01
    • 2013-02-03
    • 2022-01-12
    • 2022-11-22
    相关资源
    最近更新 更多