由于 "CORS preflight request",NodeJS API 创建了两个会话

NodeJS API creates two sessions because of "CORS preflight request"

我目前正在用 NodeJS 开发一个 API,在 VueJS 中开发一个 WebClient。 我想创建一个简单的 login/logout 机制。 Webclient 应该将请求发送到 API 并且 API 应该处理不同用户的 sessions 并从其 mongoDB.

提供数据

最近遇到一个奇怪的问题。当我想通过 WebClient 登录时,浏览器显示它向 API 发送 两个不同的 headers。一个“OPTIONS”header 和一个“POST”header。 POST header 是由于我的 POST-Request (WebClient) 发送的,很清楚。由于 Mozillas 解释,我也理解 OPTION header 部分,因为浏览器想知道 API 的 CORS-configuration 是否已针对 WebClient 配置(或类似这样的东西)。

但是现在的问题是:

由于两个不同的 header-methods,我的 API 创建了两个 session-IDs 只有一个 login-post 操作(通过 WebClient),而这两个 session 之一与 WebClient 分离,不必要地消耗有价值的 space。这仅通过 WebClient 发生。使用 PostMan 不会显示此行为,只会创建一个 session,因为只发送一个 header。

我想知道的是:

既然发送 OPTIONS-header 是有原因的,我想知道如何防止我的 API 通过 WebClient 创建第二个 session。

由于这个问题是在测试我的 WebClient 后发生的,我很清楚 WebClient 没有配置或编写不正确,但我不知道哪里如何 来防止这种情况,因为这个级别的 WebDev 对我来说是新的。比如:我必须配置我的 WebClient 还是 API?

如果需要更多代码,请告诉我您需要什么,我将编辑此 post 并附上所需代码。

//////////////////代码:

//// API:

// src/main.js:

const corsOptions = {
    origin: "http://localhost:8080",
    credentials:true,
    methods: "GET,HEAD,POST",
    preflightContinue: false,
    optionsSuccessStatus: 204
};

// src/routes/LoginRoute.js:

router.post("/login", function(req,res,next){
  console.log("// Routes/Login");
  
  if(!req.user){
    console.log("---- Info: User is not logged in");
    
    passport.authenticate("local", (err, user, info) => {
      if (err) {
        return next(err);
      }

      if (!user) {
        return res.status(401).json({success:false,errors:["User not found"]});
      }
      
      req.login(user, (err) => {
        if(err){
          return next(err);
        }
        console.log("---- Info: Routing success");
        return res.status(200).json({success:true});
      });

    })(req, res, next);
  }else{
    console.log("---- Info: User is already logged in");
    return res.status(403).json({success:false,errors:["Already logged in"]});
  }
});

//// VueJS

// src/store/index.js

actions:{
    authenticate({commit},formData){
      console.log("---- Info: Vuex/Store/Action/Authenticate - Trying to log in");
      var url = "http://localhost:3000/api/login";
      console.log(formData);
      return Vue.axios.post(url,formData)
      .then((res)=>{
        console.log("---- Info: Vuex/Store/Action/Authenticate - Success");
        commit('login',res.data);
        return res.data;
      })
      .catch((err)=>{
        console.log("---- Error: Vuex/Store/Action/Authenticate");
        console.log(err);
        // commit('logout');
        return err;
      });
    }
}

////////////////// FireFox 网络分析:

我相信您显然使用的 cors 包可以解决这个问题。

在任何情况下,这都不是您前端的问题,它由您的后端处理,浏览器通常会产生 Postman 不存在的问题。 Postman 的构建是为了不关心浏览器的 CORS 问题。但是,您可以在 Postman 中设置 session 进行测试:https://blog.postman.com/sessions-faq/

回到问题:一种方法是对所有或特定路由使用中间件功能来过滤掉已经包含 session.

的请求
    app.use((req, res, next) => {
      if (req.session) {
        // bypass new session creation, re-route or other solution
      }
      next()
    })

另一种更灵活的方法是直接针对 OPTIONS header。我使用专门针对 OPTIONS header 的请求处理程序解决了无服务器代理功能中的类似问题。它过滤掉这样的请求,returns 一个“OK 信号”和慷慨的 headers 告诉浏览器它可以继续处理真正的请求,POST。

您可以尝试像这样的东西作为通用中间件,或将其添加为对某些端点的响应(代码未测试,只是在此处随意使用 Express 语法):

    const optionsHeaders = {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Headers': 'Content-Type'
    }

    exports.reqHandler = async(req, res) => {
      if (req.method === 'OPTIONS') {
        return {
          res.set(optionsHeaders)
          res.status(200)
        }
      }

     req.session.user = {}
     req.session.user.id =  uuid.v4()
     // etc. ...

      return res.status(200).json({
        success: true,
        data: req.session.user
        msg: 'Session ID set'
      })

    }

所以我想出了如何防止浏览器向 API 服务器发送两个 header。

我必须配置 axios.post() 给函数一个 header 选项:

const axiosHeaders = {
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
};

Vue.axios.post(url,QueryString.stringify({username:username}),axiosHeaders)
.then(..)
[...]

"Configure-Type" 设置为 "application/x-www-form-urlencoded" 即可,但 form-data 必须用 QueryString.stringify(..) 函数包装。

有了这个,浏览器停止发送多个 header 和一个 post-request,在我的例子中,它停止在我的 API.[=11= 上创建多个会话]

我希望这对其他人也有用。