mongoose post.save 在heroku上失败,在本地主机上工作

Posted

技术标签:

【中文标题】mongoose post.save 在heroku上失败,在本地主机上工作【英文标题】:mongoose post.save failing on heroku, works on localhost 【发布时间】:2021-04-16 21:24:19 【问题描述】:

我正在尝试通过 node、express、mongoose 和 heroku 简单地发布到我的 MongoDB Atlas db。邮递员 POST 请求,带有正文的原始 JSON:


    "title": "heroku post",
    "description": "post me plsssss"

使用这个确切的代码在 localhost 上工作,但是当通过 heroku 上传时,try/catch 块在 post.save() 处失败,因为响应是错误。


    "error": "there's an error",
    "message": 

但是错误是空的,我不知道如何调试它。我已经在app.js 中输入了mongoose.set('debug', true);,并且我修改了我的package.json:"start": "node app.js DEBUG=mquery",,但是这些并没有产生我所看到的额外输出。有没有其他方法可以知道为什么 post.save() 会引发错误,一些我没有使用的日志..以及如何查看这些日志?或者,如果您只是知道问题所在?

App.js

//use dotenv to store secret keys
require("dotenv").config();
const express = require('express');
const mongoose = require('mongoose');
const app = express();
const cors = require('cors');

//connect to DB
mongoose.connect("mongodb+srv://grushevskiy:intercom@cluster-rest.4luv0.mongodb.net/cluster-rest?retryWrites=true&w=majority",  useNewUrlParser: true, useUnifiedTopology: true, dbName: "cluster-rest" , () => 
  console.log('connected to DB!')
)

mongoose.set('debug', true);

const db = mongoose.connection;

db.once('open', () => 
    console.log('connection opened')
);


//import routes for middleware
const postsRoute = require('./routes/posts');

//midleware routes
app.use('/posts', postsRoute)

//ROUTES
app.get('/', (req,res) => 
  res.send('we are on home')
)


//MIDDLEWARES
//cors
app.use(cors());
//decode url special characters
app.use(express.urlencoded( extended: true ));
//parse json POSTs
app.use(express.json());


//How do we start listening to the server
app.listen( process.env.PORT || 3000);

posts.js

const express = require ('express')
const router = express.Router();
const Post = require('../models/Post')

//SUBMIT A POST
router.post('/', async (req,res) => 
  const post = new Post(
    title: req.body.title,
    description: req.body.description
  );

  console.log(post)

  try 
    const savedPost = await post.save();
    res.json(savedPost);
    console.log(savedPost)
   catch (err) 
    res.json( error: "there's an error", message: err, )
  
)

module.exports = router;

Post.js 模型

const mongoose = require('mongoose')
const PostSchema = mongoose.Schema(
  title: 
    type: String,
    required: true
  ,
  description: 
    type: String,
    required: true
  ,
  date: 
    type: Date,
    default: Date.now
  
)
module.exports = mongoose.model('Post', PostSchema)

当我输入heroku logs --tail 时没有错误,最初也是“连接到数据库!”消息来晚了..我想知道这是否是异步/等待的问题?我的 package.json:


  "name": "22-npmexpres-s-restapi",
  "version": "1.0.0",
  "engines": 
    "node": "14.15.3",
    "npm": "6.14.9"
  ,
  "description": "",
  "main": "index.js",
  "scripts": 
    "start": "node app.js DEBUG=mquery",
    "start:dev": "node app.js"
  ,
  "author": "",
  "license": "ISC",
  "devDependencies": 
    "express": "^4.17.1",
    "nodemon": "^2.0.7"
  ,
  "dependencies": 
    "dotenv": "^8.2.0",
    "express": "4.17.1",
    "cors": "^2.8.5",
    "mongoose": "^5.11.11"
  

【问题讨论】:

连接可能已断开。尝试在你的 mongoose 连接函数中添加一个 catch 错误,或者将它包装在一个 try/catch 中,看看它是否成功连接。 你可以试试res.json( error: "there's an error", message: JSON.stringify(err) )吗? Alejandro 不知道该怎么做.. thammada.ts.. 没有改变输出 【参考方案1】:

当我添加错误的连接字符串时,我可以使用您的代码获得一个空的错误消息。

此回调中的console.log 不正确:

mongoose.connect("mongodb+srv://xxxxx:xxxxx@cluster-rest.4luv0.mongodb.net/cluster-rest?retryWrites=true&w=majority",  useNewUrlParser: true, useUnifiedTopology: true, dbName: "cluster-rest" , () => 
  console.log('connected to DB!')
)

如果你想知道你是否连接到数据库,请使用:

mongoose.connect("mongodb+srv://xxxxx:xxxxx@cluster-rest.4luv0.mongodb.net/cluster-rest?retryWrites=true&w=majority",  useNewUrlParser: true, useUnifiedTopology: true, dbName: "cluster-rest" );

const db = mongoose.connection;

db.once('open', () => 
    console.log('connection opened')
);

我注意到您正在使用 dotenv,也许您没有向 Heroku 主机添加变量。

【讨论】:

连接打开日志没有发生,这是什么意思?但“连接到数据库!”确实被记录了..肯定是同一件事 您传递给 mongoose.connect 的 MongoDB 地址错误。 但控制台记录的回调“已连接到数据库!”运行如此肯定它正在连接,这来自猫鼬文档:connect() 函数还接受一个回调参数并返回一个承诺。 mongoose.connect(uri, options, function(error) // 检查初始连接错误。回调没有第二个参数。); 看起来你用我的代码编辑了你的问题。我猜你没有检查错误。 我认为这只是需要成为帖子??:module.exports = mongoose.model('Post', PostSchema) 或者我的数据库连接字符串错误.. 浪费了这么多时间 gaah【参考方案2】:

仔细阅读您的问题后,我有几个建议,我认为您可能想尝试一下。首先,让我解释一下自己:

根据我的经验,当学习如何使用 Node.js 构建 REST API,使用 mongoose 与 MongoDB 集群通信时,他们的index.js 文件的事件序列如下所示:

    执行一个使用环境变量建立与数据库集群的连接的函数。此函数仅运行一次,并填充应用程序将要使用的任何为其设计的模型的 de mongoose 实例。 通过要求适当的包、定义它需要的任何中间件并调用为其定义的所有路由来设置应用程序。 在应用程序整合了正常运行所需的一切后,app.listen() 被调用,提供了一个有效的端口,然后......瞧!您的服务器正在运行。

当应用程序和数据库足够简单时,这就像一个魅力。我已经使用这些相同的步骤构建了多个服务并将它们投入生产,而没有注意到 mongoose 和应用程序之间存在任何错误通信。但是,如果您检查您提供的 App.js 代码,您会发现您的情况有所不同:您只有在设置应用程序、它的中间件和它的路由之后才连接到您的 Mongo 集群。如果其中任何一个依赖于 mongoose 实例或要运行的模型,那么很有可能当 Heroku 的编译器到达您的路由需要连接到数据库的地步时,它根本没有实现该连接(意思是没有运行 mongoose.connect() 部分)jet。

简单的解决方案

我认为 Heroku 运行整个代码的时间要比本地机器长一点。此外,如果您的本地版本连接到本地存储的 MongoDB,那么运行速度很可能比在云服务中快得多。你的陈述

“连接到数据库!”消息来晚了

给了我希望,情况可能就是这样。如果是这样,那我猜是移动

mongoose.connect("mongodb+srv://xxxxx:xxxxx@cluster-rest.4luv0.mongodb.net/cluster-rest?retryWrites=true&w=majority", useNewUrlParser: true, useUnifiedTopology: true, dbName: "cluster-rest" , () => console.log('connected to DB!'));

App.js 的第二行,就在你调用Dotenv 之后,就可以了。您的 mongoose 实例将进入已与 App 将要使用的数据库和模型连接的 App。

异步解决方案

即使我之前写的内容解决了您的问题,我也想扩展解释,因为根据我的经验,该版本的解决方案也不正确。我在这个答案开头暴露的做事方式是可以的......只要你保持你的MongoDB集群和你的猫鼬实例简单。不久前,我不得不实现一个涉及更复杂数据库模式的服务,同一个 App 使用多个不同的数据库,并且它的许多路由直接取决于这些数据库的模型。

当我尝试使用我之前描述的逻辑来解决这样一个问题时,涉及到大量填充的数据库和 mongoose 实例中包含的几个模型,我意识到 Node 构建应用程序、它的中间件和它的路由所花费的时间远远少于连接到猫鼬并加载同一个应用程序运行所需的所有模型。这意味着我在实际连接数据库之前收到了Server connected to PORT **** 消息。这引起了麻烦。

这让我意识到,正确做事的唯一方法是确保异步性,强制应用程序仅在所有数据库和模型都加载到猫鼬实例中后运行,并将相同的实例提供给它。这样,我最终得到了一个看起来像这样的index.js

const Dotenv = require("dotenv").config();
const  connectMongoose  = require("./server/db/mongoose");
const wrappedApp = require("./app");

connectMongoose().then((mongoose) => 
  const App = wrappedApp(mongoose);

  App.listen(process.env.PORT, () => 
    console.log(
      `Hello, I'm your server and I'm working on port $process.env.PORT`
    );
  );
);

一个包含多个函数的文件,用于管理与我的数据库和模型的连接,名为mongoose.js。以正确顺序实现所有连接的函数看起来是:

const connectMongoose = async (mongoose = require("mongoose")) => 
  try 
    // Close any previous connections
    await mongoose.disconnect();

    // Connect the Main database
    console.log(
      "--> Loading databases and models from MongoDB. Please don't use our server jet. <--"
    );
    mongoose = await connectMother(mongoose);

    // Connect all the client databases
    mongoose = await connectChildren(mongoose);

    console.log(
      "--> All databases and models loaded correctly. You can work now. <--"
    );

    return mongoose;
   catch (e) 
    console.error(e);
  
;

module.exports =  connectMongoose ;

这样,app.js 文件返回一个函数,该函数需要输入一个 mongoose 实例并设置所有 Express 环境:

module.exports = (Mongoose) => 
  const Express = require("express"); //Enrouting
  // Other required packages go here

  // EXPRESS SETTINGS
  const App = Express();
  App.set("port", process.env.PORT || 3000); //Use port from cloud server

  // All the middleware App needs
  App.use(Flash());

  // Routing: load your routes
  
  // Return fully formed App
  return App;
;

只有使用这种模式,我才能确保事件链有意义:

    连接所有数据库并加载它们的模型。 只有在连接最后一个数据库并加载其最后一个模型后,才能设置应用程序、其中间件和您的路由。 使用 App.listen() 为应用提供服务。

如果您的数据库不是很大,那么答案的第一部分可能会奏效。但了解整个故事总是有用的。

【讨论】:

非常感谢您提供这么多详细信息.. 第一部分不起作用.. 使用异步方式,您的 connectMother 函数是什么样的.. 我很难理解如何移植我的图案到你的身上 嗨!抱歉,我忘了解释 connectMother 和 connectChildren 函数。正如我所提到的,在这种情况下,我必须连接到多个数据库,它们之间存在交叉依赖关系,并且我需要确保连接是按顺序完成的。这些函数本质上实现了 mongoose.connect() 并将我为每个数据库定义的所有模型加载到 mongoose 实例中。这一切顺利吗?

以上是关于mongoose post.save 在heroku上失败,在本地主机上工作的主要内容,如果未能解决你的问题,请参考以下文章

mongoose post方法总结and疑点

在 Mongoose 保存后挂钩中捕获并忽略/抑制错误

NodeJS + SocketIO 大套接字事件管理

从 django post save 信号产生一个线程可以吗?

RestfulController POST(save)方法中动态字段的映射

django事务中发信号怎么保证post save执行时数据已经写入数据库