Javascript 选项请求不一致(在部署中)? 介绍

Javascript 选项请求不一致(在部署中)? 介绍,javascript,node.js,reactjs,express,axios,Javascript,Node.js,Reactjs,Express,Axios,因此,我正在使用MERN堆栈(与Heroku+Netlify一起使用),并且在处理删除请求时遇到了一些非常奇怪的一致性问题。在过去的几年里,我尝试了无数的解决方案 三天来,我一直在努力让这一切顺利进行,但都没有成功。这些解决方案中有很多都是有效的 来自stack overflow,所以如果你想让我转到另一个帖子,我可能已经看到了。我已经搜索了网络的每一个部分,写这篇文章是我最后的选择 问题 因此,当我发出一个删除请求时,我得到的是每个常用选项请求,因为我在请求的自定义头中发送了一个令牌(“x-a

因此,我正在使用MERN堆栈(与Heroku+Netlify一起使用),并且在处理删除请求时遇到了一些非常奇怪的一致性问题。在过去的几年里,我尝试了无数的解决方案 三天来,我一直在努力让这一切顺利进行,但都没有成功。这些解决方案中有很多都是有效的 来自stack overflow,所以如果你想让我转到另一个帖子,我可能已经看到了。我已经搜索了网络的每一个部分,写这篇文章是我最后的选择

问题 因此,当我发出一个删除请求时,我得到的是每个常用选项请求,因为我在请求的自定义头中发送了一个令牌(“x-auth-token”)。选项请求总是以204来解析,这意味着一切都应该正常。然而,之后,并没有像应该的那个样的删除请求。这在本质上是我的问题。我检查了我的Heroku日志,只看到选项请求,没有其他内容

矛盾? 这就是我一直很困惑的地方。问题是,有时候它确实有效。我在API中使用的其他路由(如登录和创建新帖子)也可以工作,尽管我使用的是相同的中间件。 每次它工作时,我都会收到OPTIONS请求,然后是DELETE请求(状态为200),就像我预期的那样

如果您想要一个可重新创建的场景示例:

在登录并获得有效令牌后,我创建了X个数字的帖子,然后我可以在主页上的帖子列表中看到这些帖子呈现。然后我浏览其中一篇文章,然后单击确认按钮将其删除。我会自动被重定向到列表中的下一篇文章。我重复这个直到我到达最后一站。我删除了那个帖子,因为已经没有帖子了,我被重定向到了帖子列表,它是。。。不是空的!我尝试删除的最后一篇帖子仍然存在

请记住,所有的删除请求都是以完全相同的方式发送的,因此我非常确定这不是前端问题,因此无需在代码中进行修改。我已经记录了所有内容并进行了调试,它与我预期的完全一致

(create post不会重定向,而delete post会重定向?我看不出这会对任何事情产生什么影响,因为delete请求会按常规发送……尽管可能有一个解决方案就在这个事实中。)

我尝试过的解决方案 科尔斯 首先,你可能已经冲到键盘前告诉我这是一个CORS问题。我昨天也这么想,但现在不太确定。我尝试过在CORS中搞乱所有可能的配置设置,以使其正常工作。因为我的两个网站位于不同的域上,所以CORS会验证请求。我已经将我的前端网站添加到了白名单中,所有其他请求都正常通过,所以没有问题。我尝试在配置中添加Alloweaders选项,但除了默认设置之外,它没有做任何事情。我还向配置中允许的方法添加了“选项”,但仍然没有。我还使用app.use(cors({config}))。稍后我将包含一些代码,以详细了解其中的一些内容

调试 我基本上通过在所有地方插入console.logs进行了测试,发现当options请求没有导致DELETE请求时,中间件、options路由(我尝试使用相同的路由url创建一个options路由)或原始post路由都不会被执行

静态服务器 这可能是我的一些经验不足之处(这是我的第一个网络项目)。我看到一些解决方案告诉我们需要一个静态服务器。所以我尝试设置一个静态服务器,但没有看到任何结果。所以我不太确定这是怎么实现的

异步并等待 此时我只是在尝试,所以我将所有路由设置为异步,以查看它是否有任何作用。没有

其他 我还搞乱了环境变量和dotenv,以及其他我记不起来的东西。我认为这里的一切应该已经足够了解情况了

代码 index.js

const express = require('express');
require("dotenv").config({ path: "variables.env" });
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const routes = require("./routes/router");
const cors = require("cors");
const morgan = require('morgan')

const app = express();

const whitelist = [
    process.env.ORIGIN
];

app.use(
cors({
    origin: function (origin, callback) {
        if (whitelist.indexOf(origin) !== -1) {
            callback(null, true);
        } else {
            console.log(origin);
            callback(new Error("Not allowed by CORS"));
        }
    }, //frontend server localhost:3000
    methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
    credentials: true, // enable set cookie
}));

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(morgan('dev'));



mongoose.connect(process.env.MONGODB_URL, {
    useNewUrlParser: true,
    useCreateIndex: true,
    useUnifiedTopology: true
});

const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
  console.log('connected to db');
});

const userSchema = mongoose.Schema({
    name: String,
    password: String
});

// Routes
// TODO: make seperate routers/routes
app.use("/", routes);

// Serve static assets if in production
if (process.env.NODE_ENV === 'production') {
    // Set static folder
    app.use(express.static('client/build'));

    app.get('*', (req, res) => {
        res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
    });
}

// TODO: set up custom port in future
app.listen(process.env.PORT, () => console.log(`Server listening at http://localhost:${process.env.PORT}`));
// Callback functions?
路由器.js

const express = require('express');
const router = express.Router();
const Post = require('../models/Post');
const User = require('../models/User');
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const auth = require('../middleware/auth');
const adminAuth = require('../middleware/adminAuth');
const cors = require("cors");

require("dotenv").config({ path: "variables.env" });
// import 'moment'

// second onwards are handlers => triggers like the post body then next() to go to the next handler
router.post('/api/add_post', adminAuth, async (req, res, next) => {
  try{
    
    newPost = new Post({
      title: req.body.title,
      body: req.body.body,
      author: req.body.author,
      created: req.body.created,
    });

    const savedPost = await newPost.save();
    if (!savedUser) throw Error('Something went wrong saving the post');
    res.send(savedPost);
  } catch (e) {
    res.status(400).json({ msg: e.message });
  }
    
});

router.delete('/api/delete_post/:id', adminAuth, async (req, res, next) => {
  // timeout?
  // console.log(req.body);
  try{
    const id = req.params.id;
    if(!id) throw Error('Invalid ID');
    const post = await Post.findById(id);
    if (!post) throw Error('Post doesn\'t exist');
    const removed = await post.remove();
    if(!removed) throw Error('Problem with deleting the post');
    res.status(200).json({ success: true });
  } catch(e) {
    console.log("Error: ", e.message);
    res.status(400).json({ msg: e.message, success: false });
  }
  
});


// TODO : UPDATE for async soon
router.post('/api/update_post', adminAuth, async (req, res, next) => {
  const id = req.body._id;
  test_post_data = {
      title: req.body.title,
      body: req.body.body,
      author: req.body.author,
      modified: req.body.modified,
  };
  console.log(test_post_data, id);
  Post.updateOne({ _id: id }, test_post_data, (err) => {
      if(err) return next(err);
      return res.status(200);
  });
});

router.get('/api/get_posts', async (req, res, next) => {
  try{
    const posts = await Post.find();
    if(!posts) throw Error('Error with fetching the posts')
    res.send(posts.reverse());
  } catch (e) {
    res.status(400).json({ msg: e.message });
  }
});

router.get('/api/get_chapter/:id', async (req, res, next) => {
  try{
    const id = req.params.id;
    const post = await Post.findOne({_id: id})
    if(!post) throw Error('No post was found')
    res.send(post);
  } catch(e) {
    res.status(400).json({ msg: e.message })
  }
    
});



// User routes
// TODO : make in seperate file

  router.post('/api/user/register', async (req, res) => {
    const { name, email, password } = req.body;
  
    // Simple validation
    if (!name || !email || !password) {
      return res.status(400).json({ msg: 'Please enter all fields' });
    }
  
    try {
      const user = await User.findOne({ email });
      if (user) throw Error('User already exists');
  
      const salt = await bcrypt.genSalt(10);
      if (!salt) throw Error('Something went wrong with bcrypt');
  
      const hash = await bcrypt.hash(password, salt);
      if (!hash) throw Error('Something went wrong hashing the password');
  
      const newUser = new User({
        name,
        email,
        password: hash,
        admin: false
      });
  
      const savedUser = await newUser.save();
      if (!savedUser) throw Error('Something went wrong saving the user');
      

      // TODO : check up on expires stuff : 3600 = 1 hr
      const token = jwt.sign({ id: savedUser._id, admin: savedUser.admin }, process.env.JWT_SECRET, {
        expiresIn: 3600
      });
  
      res.status(200).json({
        token,
        user: {
          id: savedUser.id,
          name: savedUser.name,
          email: savedUser.email,
          admin: savedUser.admin
        }
      });
    } catch (e) {
      res.status(400).json({ error: e.message });
    }
  });

  router.post('/api/user/login', async (req, res) => {

    const { name, password } = req.body;
  
    // Simple validation
    if (!name || !password) {
      return res.status(400).json({ msg: 'Please enter all fields' });
    }
  
    try {
      // Check for existing user
      const user = await User.findOne({ name });
      if (!user) throw Error('User Does not exist');
  
      const isMatch = await bcrypt.compare(password, user.password);
      if (!isMatch) throw Error('Invalid credentials');
  
      const token = jwt.sign({ id: user._id, admin: user.admin }, process.env.JWT_SECRET, { expiresIn: 3600 });
      if (!token) throw Error('Couldnt sign the token');
  
      res.status(200).json({
        token,
        user: {
          id: user._id,
          name: user.name,
          email: user.email,
          admin: user.admin
        }
      });
    } catch (e) {
      res.status(400).json({ msg: e.message });
    }
  });


module.exports = router;
adminAuth.js

const jwt = require('jsonwebtoken')
require("dotenv").config({ path: "variables.env" });

module.exports = (req, res, next) => {
  console.log(req.header('x-auth-token'));
  const token = req.header('x-auth-token');

  // Check for token
  if (!token)
    return res.status(401).json({ msg: 'No token, authorizaton denied' });

  try {
    // Verify token
    const decoded = jwt.verify(token, process.env.JWT_SECRET);

    console.log('decoded:', decoded);
    
    if(!decoded.admin)
        return res.status(401).json({ msg: 'Not an admin, authorization denied' });

    // Add user from payload
    // console.log('decoded:', decoded);
    req.user = decoded;
    next();
  } catch (e) {
    res.status(400).json({ msg: 'Token is not valid' });
  }
};
链接以获取请求示例和Heroku日志,因为Stackoverflow说这是垃圾邮件:

编辑

我将Chrome请求和响应标题粘贴到底部的gist中,但是没有响应数据

我已经调试了一点,用它来检查差异,我发现当delete操作结束时,red(cancelled)请求有头,而非working请求则完全为空(如果这意味着什么的话,则用“临时头”填充)

我无法将请求标题复制粘贴到工作红色(已取消)标题的要点中。但是,我粘贴了所有我认为可能对chrome有用的东西,希望它能有所帮助


另外,我在使用Chrome网络工具时没有看到任何删除请求,我在另一个工具上看到了它们。不确定这是否重要,可能只是某个配置选项。

所以,我还没有找到确切的答案,但我找到了解决办法

事实证明,这可能与axios有关,在过去的3天里,我一直在寻找错误的东西

这条线帮助我:

我在用于删除按钮的onClick方法中添加了一个
e.preventDefault()

这修复了问题,但没有重定向(我使用
href={link}
),因此我将为react router添加一个条件呈现来重定向页面。我不知道有更好的方法,所以也许能给我一些想法。如果我有进一步的问题,我会编辑。

因此,我还没有找到确切的答案,但我找到了解决办法

事实证明,这可能与axios有关,在过去的3天里,我一直在寻找错误的东西

这条线帮助我:

我在onClic中添加了一个
e.preventDefault()