我正在尝试创建一个简单的网页,提示用户输入时间并按提交。提交后,我希望与该集合相对应的数据显示在网页上。
当我单击提交按钮时。它节省了我插入相应Mongo集合的时间。实际上,我什至可以用console.logging来显示整个集合。我只是无法让它显示在网页上。
我是NodeJS和MongoDB的新手,所以请多多包涵。
这是index.ejs文件。客户是保存时间的集合名称。
<div>
<ul class="clients">
<% for(var i=0; i< clients.length; i++) {%>
<li class="client">
<span><%= clients[i].time %></span>
</li>
<% } %>
</ul>
</div>
</head>
<body>
<form action="/clients" method="POST">
<input type="text" placeholder="time" name="time">
<button type="submit">Submit</button>
</form>
我的app.js中有此文件,它可以将插入的时间成功地发布到集合中。
app.post('/clients', (req, res) => {
var url = "mongodb://localhost:27017/lesson-data";
mongoose.connect(url, function (err, db) {
if (err) throw err;
db.collection('clients').save(req.body, (err, result) => {
if (err) return console.log(err)
console.log('saved to database')
res.redirect('/')
});
});
});
这在我的路线中> index.js-成功将时间记录到控制台,但不会显示在网页上。
router.get('/', function(req, res, next) {
var url = "mongodb://localhost:27017/lesson-data";
mongoose.connect(url, function (err, db) {
if (err) throw err;
db.collection('clients').find().toArray((err, result) => {
if (err) return console.log(err);
console.log(result );
console.log("chea");
// renders index.ejs
res.render('index', {clients: result});
});
});
});
我在做什么错?我觉得自己已经接近了,已经花了很多时间尝试解决这个问题。
答案 0 :(得分:2)
因此,这里不仅有很多错误,而且最好是从头开始编写一个小型应用程序来解释问题。
您要做的第一件事是选择一个文件夹并为项目创建一个空间。您将在项目中需要一些子文件夹,以便您可以通过bash
进行以下操作:
mkdir -p ejsdemo/{models,routes,views/pages}
如果您是在Windows上执行此操作,则可以执行任何想要创建类似结构的操作,但是您基本上希望在顶层ejs-demo
文件夹中出现类似的内容:
.
├── models
├── routes
└── views
└── pages
然后,您要初始化nodejs项目并安装依赖项。您可以通过以下操作再次执行此操作:
cd ejs-demo
npm init -y && npm i -S express ejs mongoose morgan body-parser
根据您所使用的操作系统的不同,再次可能有所不同,但是您想要的是node_modules
文件夹中已安装的ejs-demo
和一个基本上为以下内容的package.json
文件:>
{
"name": "ejsdemo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.18.3",
"ejs": "^2.6.1",
"express": "^4.16.4",
"mongoose": "^5.4.20",
"morgan": "^1.9.1"
}
}
您可以选择仅基于文件夹中的内容创建package.json
并运行npm i
,这基本上是npm install
的缩写,并且将安装所有内容。
现在应该在models
子文件夹中,添加基本列表。 Mongoose ODM(对象文档映射器)实际上具有为定义“模式”的集合注册“模型”的概念,并且还可以出于特殊目的而强制执行其他验证约束甚至特殊的实例方法或“静态”类方法。 >
将这些视为您的集合的“包装器”,实际上包括用于执行许多常见操作的辅助程序并减少样板。我们在这里仅使用一个非常简单的模型进行演示,我们将其命名为:
models / client.js
const { Schema } = mongoose = require('mongoose');
const clientSchema = new Schema({
name: String,
time: String
});
module.exports = mongoose.model('Client', clientSchema);
这是非常基本的操作,只需导入Schema
帮助函数即可定义“模式”,该模式与实际注册模型的mongoose.model()
函数一起使用。
这是在此“模块”中所需要的全部,我们将在要使用此模型的其他模块中require()
将此文件require()
。请注意,我们不需要在这里了解连接。
通常,您希望从主应用程序逻辑中抽象出路由处理程序,并且有一种简单的方法可以做到这一点。按照您的示例,我们将在模块中创建两条路由,然后再次在适当的位置const express = require('express');
const router = express.Router();
const Client = require('../models/client');
router.get('/', async (req, res, next) => {
try {
let clients = await Client.find();
console.log(clients);
res.render('pages/index', { clients });
} catch (e) {
next(e);
}
});
module.exports = router;
:
routes / root.js
const express = require('express');
const router = express.Router();
const Client = require('../models/client');
router.post('/', async (req, res, next) => {
try {
console.log(req.body);
await Client.create(req.body);
res.redirect('/');
} catch (e) {
next(e);
}
});
module.exports = router;
routes / clients.js
Client
这两个都是非常简单的例子。注意它们如何从先前创建的模型中导入async/await
。两者都有一个分别是GET和POST的方法,并尝试使用“ root”路径。这是到最终端点的相对路由,稍后将进行注册。但是这种结构允许定义“子路由”和其他Http“动词”动作。
我正在使用NodeJS 8.x及更高版本中的async/await
演示所有这些内容。如果您正在学习,那么这应该是您正在运行的最低版本。如果适合您的样式,则可以选择使用回调或简单的Promise,但是现代的.find()
语法通常可以使代码更清晰,更易于阅读,而您的同级会感谢您。
在两种情况下,都非常简单地从模型中调用.create()
或await
,只需使用async
“等待”,因为它们各自返回Promise,您可以这样做。注意每个函数处理程序的定义前的async
。将结果标记为await
之前,必须先将其标记为.find()
。
Array
当然只是返回集合中的所有数据,并且由于它是模型上的猫鼬方法,为方便起见,它已经以.create()
的形式返回。同样,insertOne()
基本上是req.body
的包装,可以选择遍历要创建的文档数组,并实质上“保存”到集合中。这只是使用<div>
<ul class="clients">
<% for ( let client of clients ) { %>
<li class="client">
<span><%= client.name %>
<span><%= client.time %>
</li>
<% } %>
</ul>
</div>
<form action="/clients" method="POST">
<input type="text" placeholder="name" name="name">
<input type="text" placeholder="time" name="time">
<div>
<button type="submit">Submit</button>
</div>
</form>
,在实际调用此路由时,它将包含带有一些“已发布”表单内容的JavaScript对象。
此外,您需要设置视图模板。再次可以涉及到这一点,但是为了进行简单的演示,我们将只使用一个与问题中的模板相似的基本模板:
视图/页面/index.ejs
for..of
我什至不理会样式或任何其他包装HTML结构。一个简单的列表和一个表格足以演示。还要注意现代的index.js
循环,它比按索引引用数组元素要干净得多。 EJS基本上在模板中支持JavaScript。因此,如果它是有效的JavaScript,则对模板使用有效。在合理范围内:
基本上剩下的就是放置在项目根文件夹中的主const mongoose = require('mongoose');
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const Client = require('./models/client');
const rootRoutes = require('./routes/root');
const clientRoutes = require('./routes/clients');
const uri = 'mongodb://localhost:27017/lesson-test';
const opts = { useNewUrlParser: true };
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
mongoose.set('debug', true);
const app = express();
app.set('view engine', 'ejs');
app.use(morgan('combined'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use('/', rootRoutes);
app.use('/clients', clientRoutes);
(async function() {
try {
const conn = await mongoose.connect(uri, opts);
// Clean data for demo
await Promise.all(
Object.entries(conn.models).map(([k, m]) => m.deleteMany())
);
// Insert some starter sample
await Client.insertMany([
{ name: 'One', time: '2:00' },
{ name: 'Two', time: '3:00' }
]);
app.listen(3000);
} catch (e) {
console.error(e)
}
})()
文件。实际上,我们要做的就是加载我们先前创建的某些模块,注册端点以建立数据库连接并启动http侦听器。这主要是顺序的,但是我们可以完成一些事情:
index.js
mongoose
在清单的顶部,仅是我们初始化项目时安装的主要模块中的一个块。当然,这包括connect()
,因为我们想express
到MongoDB,而morgan
是因为我们需要设置应用程序的主要处理程序。诸如bodyParser
之类的其他内容只是为了在控制台中显示一些“日志记录”以确认请求,而const Client = require('./models/client');
const rootRoutes = require('./routes/root');
const clientRoutes = require('./routes/clients');
之所以非常重要,是因为我们稍后需要解码来自表单的POST请求。>
下一部分:
Client
这只是导入我们之前创建的“模块”。通常,您不希望index.js
或此类uri
清单中的其他模型,但是对于本演示,我们将为第一个请求准备好一些数据。其他人导入我们之前设置的路由处理程序。
清单的下一部分实际上只是为猫鼬设置的,大部分都是可选的。这里唯一重要的真实事物是用于实际连接的opts
和uri
设置。这些仅在示例列表的顶部附近,以防app.set('view engine', 'ejs');
app.use(morgan('combined'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use('/', rootRoutes);
app.use('/clients', clientRoutes);
需要更改MongoDB连接。请注意,该演示是“独立的”,因此请勿将其指向任何现有数据库,因为它期望使用的名称不会被使用。
然后是快速设置:
ejs
第一行没有其他设置就为模板注册morgan
,因此默认位置用于我们已经定义的位置。 bodyParser
行设置了请求日志记录中间件,两个/
调用也分别注册了用于JSON解析和UrlEndcoded内容的中间件,后者是HTML Form帖子的默认设置。
最后两行将这些导入用于路由处理程序,并将其分配给最终端点。这就是为什么在定义本身中两个请求处理程序都使用app.use()
的原因,因为这与(async function() {
try {
const conn = await mongoose.connect(uri, opts);
// Clean data for demo
await Promise.all(
Object.entries(conn.models).map(([k, m]) => m.deleteMany())
);
// Insert some starter sample
await Client.insertMany([
{ name: 'One', time: '2:00' },
{ name: 'Two', time: '3:00' }
]);
app.listen(3000);
} catch (e) {
console.error(e)
}
})()
下定义的端点有关。这是很常见的做法。
接下来是主要的代码块,再次非常简单:
async
请注意,该块已标记为await
,因此我们可以在其中使用try..catch
关键字。此外,还有相同的await
块样式用于错误处理。实际上,其中的简单第一个调用将连接到MongoDB。这是正在运行的应用程序中的第一个实际的异步方法调用。因此,在我们进一步执行代码之前,请uri
。只是采用了先前定义的opts
和Promise.all( Object.entries(..).map(..) )
参数。
由于这是一个“自包含”的演示,因此,在执行任何其他操作之前,我只是从所有注册的模型中清空目标集合。并不是您通常会做的那种事情,但是require()
基本上是一种为每个注册的猫鼬模型处理某些事情的方法。如清单顶部附近所示,任何型号的“注册”都发生在开头的Client.insertMany()
中。
接下来的事情应该很明显,因为我们只是使用await
来插入一些示例数据。同样,这是一个异步函数,因此您在继续执行之前先3000
返回结果。
最后,我们应该高兴地连接到MongoDB并插入了一些示例数据,因此可以在默认情况下开始监听localhost
的端口node_modules
上的请求。< / p>
如果所有这些都准备就绪,则目录结构现在应该看起来像这样(当然省略.
├── index.js
├── models
│ └── client.js
|── node_modules
├── package.json
├── package-lock.json
├── routes
│ ├── clients.js
│ └── root.js
└── views
└── pages
└── index.ejs
下的所有详细信息):
node index.js
如果是这样并保持与上面显示的完全相同的代码,则可以使用以下代码运行:
Mongoose: clients.deleteMany({}, {})
Mongoose: clients.insertMany([ { _id: 5ca06fbc38a9b536315d732c, name: 'One', time: '2:00', __v: 0 }, { _id: 5ca06fbc38a9b536315d732d, name: 'Two', time: '3:00', __v: 0 } ], {})
然后您应该会看到以下行:
http://localhost:3000/
现在,您应该准备将浏览器打开到Mongoose: clients.find({}, { projection: {} })
[ { _id: 5ca06fbc38a9b536315d732c,
name: 'One',
time: '2:00',
__v: 0 },
{ _id: 5ca06fbc38a9b536315d732d,
name: 'Two',
time: '3:00',
__v: 0 } ]
::ffff:10.0.2.2 - - [31/Mar/2019:07:45:26 +0000] "GET / HTTP/1.1" 200 423 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
并查看分配给该路线的渲染模板。您运行应用程序的控制台应指示已点击路由:
<li>
当然,这也显示了从Mongoose到MongoDB服务器的请求。现在,应该在页面上的{ name: 'Four', time: '4:00' }
Mongoose: clients.insertOne({ _id: ObjectId("5ca0710038a9b536315d732e"), name: 'Four', time: '4:00', __v: 0 })
::ffff:10.0.2.2 - - [31/Mar/2019:07:49:20 +0000] "POST /clients HTTP/1.1" 302 46 "http://localhost:3000/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
项中呈现相同的数据。
您还可以填写表单字段并提交,这将在控制台中显示如下响应:
req.body
其中显示了解析的 insertOne()
的内容以及模型的create()
方法产生的POST
,当然还有{{1 }}请求。然后,重定向操作将返回到/
路由:
Mongoose: clients.find({}, { projection: {} })
[ { _id: 5ca06fbc38a9b536315d732c,
name: 'One',
time: '2:00',
__v: 0 },
{ _id: 5ca06fbc38a9b536315d732d,
name: 'Two',
time: '3:00',
__v: 0 },
{ _id: 5ca0710038a9b536315d732e,
name: 'Four',
time: '4:00',
__v: 0 } ]
::ffff:10.0.2.2 - - [31/Mar/2019:07:49:20 +0000] "GET / HTTP/1.1" 200 504 "http://localhost:3000/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
这些是您需要在自己的应用程序中重复的基本概念。我们在这里介绍的基本内容是:
创建模型-在其中为每个集合定义一个模型,从而可以为架构设置规则。猫鼬可以选择设置为{ strict: false }
,并且根本不调用任何模式验证或类型转换。通常比处理核心驱动程序方法友好一些。
单独的路由-可以在逻辑组中将操作和处理程序设置为需要的位置,而无需绑定到严格的端点。最终约会的设置可以在以后进行,而这个“控制器”界面实际上只是表示视图和模型之间的“握手层”。
一次连接到数据库-这是一个 重要 规则,可通过Mongoose的一般用法模式强制实施。您的基于请求的应用程序在每个请求内都没有业务连接和断开连接(就像您所做的那样)。您只能连接 ONCE 并保持打开状态。该驱动程序实际上将管理连接池之类的事情并帮助分发,因此不会阻止多个并发请求。
驱动程序将管理池中的所有其他连接,并在不需要它们时断开连接。尽管通常会有一个默认的池大小保持打开状态,但始终可以准备下一个请求。通常,您在此阶段不必担心这些,因为它是您仅在真正遇到需要了解时才需要了解的细节。这不会持续一会儿。
基本上,如果您按照此处的说明进行操作,那么您将获得一个实际的示例,该示例说明了您基本上想做的事情,并且可以“依靠”来做更大更好的事情。
玩得开心!