五一闲来无事,便想起了我第一个项目所用的数据库mongoDB,于是便去复习了一下其用法和操作,写了一个基于express框架,mongoDB数据库的简单项目,下面我将详细的对项目做个总结
项目地址:https://github.com/workerfanhaitao/node-express-mongodb-ejs
感兴趣的可以直接在git上或者pull下来看
项目逻辑主要为
(前提:这里默认是大家电脑上已经安装了node以及mongoDB数据库的,由于这块资源很多,所以这里就不重复说了)
1.项目搭建
首先进入你常用的文件夹
输入命令 npm install express-generator -g 进行全局安装
随后输入命令 express -e project 生成一个名为project(可自定义)的文件夹
cd project 进入文件夹 随后 npm install 进行依赖的安装
这边必须使用nodemon进行项目的启动(否则启动时会卡住)
npm install nodemon -g 后 将 package.json文件中的启动方式由node改为nodemon
此时使用npm start便能够成功在localhost:3000端口访问到
打开命令行,使用mongo命令连接上mongoDB服务
首先 use project 创建 project数据库
随后
db.createCollection(‘users’) 创建用户集合
db.createCollection(‘article’) 创建文章集合
2.项目构建
文件结构大致如下
首先执行 npm install mongodb -S 全局安装mongodb模块
创建model文件夹,并在其中封装连接数据库的方法
// model/index.js
var MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017'
const dbName = 'project'
//数据库链接方法封装
function connect (callback) {
MongoClient.connect(url, function(err,client) {
if (err) {
console.log('数据库连接错误', err);
} else {
var db = client.db(dbName);
callback && callback(db)
client.close();
}
})
}
module.exports = {
connect
}
在routes文件夹的index文件中将其引入并使用进行数据库连接操作
// routes/index.js
router.get('/', function(req, res) {
model.connect((db) => {
db.collection('users').find().toArray((err, docs) => {
console.log(docs);
})
})
});
这样就能取到project数据库users集合中的对象列表了,此时是没有数据的,可以手动插入数据进行测试
启动mongo后 使用db.collection(‘users’).insertOne({‘xxx’: ‘xxxx’})或是使用
MongoDB数据库可视工具手动插入 下载地址:https://robomongo.org/
新建页面前先进行准备工作
根目录views文件夹下新建head.ejs, 将通用头部以及css通用样式引入文件放入其中(需要我的样式文件的话可以去上面的git地址中复制下来,因为样式简单,所以自己写也可)
// head.ejs
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel='stylesheet' href='/stylesheets/style.css' />
随后进行注册页面的构建(ps:以下views文件夹中需要跳转的页面均需在routes.js中引入,如下)
// routes/index.js
router.get('/regist', (req, res) => {
res.render('regist', {});
})
在根目录views文件夹下新建regist.ejs文件,将head通过ejs语法引入
// regist.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<title>注册</title>
<%- include head %>
</head>
<body>
<div class="container">
<h1 class="margin-b-20">注册</h1>
<form action="/users/regist" class="margin-b-40" method="post">
<input class="margin-b-20" type="text" name="username" value="" placeholder="请输入用户名"><br>
<input class="margin-b-20" type="password" name="password" value="" placeholder="请输入密码"><br>
<input class="margin-b-20" type="password" name="password2" value="" placeholder="请确认密码"><br>
<button class="button button-regist" type="submit">注册</button>
</form>
<div>已有账号,<a href="/login">立即登录!</a></div>
</div>
</body>
</html>
接下来在routes文件夹下新建users.js文件并新增用户注册方法(ps:user.js文件别忘了在app.js中引入哦)
// app.js
var usersRouter = require('./routes/users');
app.use('/users', usersRouter);
// users.js
var express = require('express');
var router = express.Router();
var model = require('../model')
//注册接口
router.post('/regist', (req, res) => {
let data = {
username: req.body.username,
password: req.body.password,
password2: req.body.password2
}
model.connect((db) => {
db.collection('users').insertOne(data, (err, ret) => {
if (err) {
console.log('注册失败');
res.redirect('/regist')
} else {
console.log('注册成功');
res.redirect('/login');
}
})
})
})
构建登录页面前我们需要引入express-session模块
npm install express-session -S
并在app.js中进行配置登录过期时间,如果登录过期将会触发登录拦截
// app.js
var session = require('express-session');
// session配置
app.use(session({
secret: 'my project',
resave: false,
saveUninitialized: true,
cookie: {maxAge: 1000 * 60 * 60 *24} //指定登陆对话的有效时长
}))
//登录拦截
app.get('*', (req, res, next) => {
var username = req.session.username;
var path = req.path
if (path !== '/login' && path !== '/regist') {
if (!username) {
res.redirect('/login');
}
}
next();
} )
随后在views文件夹下创建登录页面login.ejs
// login.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<title>登录</title>
<%- include head %>
</head>
<body>
<div class="container">
<h1 class="margin-b-20">登录</h1>
<form action="/users/login" class="margin-b-40" method="post">
<input class="margin-b-20" type="text" name="username" value="" placeholder="请输入用户名"><br>
<input class="margin-b-20" type="password" name="password" value="" placeholder="请输入密码"><br>
<button class="button button-login" type="submit">登录</button>
</form>
</div>
</body>
</html>
同样在routes/users.js文件中加入登录方法
// routes/users.js
//登录接口
router.post('/login', (req, res) => {
let data = {
username: req.body.username,
password: req.body.password
}
console.log(data);
model.connect((db) => {
db.collection('users').find(data).toArray((err, docs) => {
if (err) {
res.redirect('/login');
console.log('账号或密码错误');
} else {
if (docs.length > 0) {
//登录成功,进行session会话存储
req.session.username = data.username;
res.redirect('/?pageIndex=' + 1);
console.log('登录成功');
} else {
res.redirect('/login');
}
}
})
})
})
views文件夹下创建bar.ejs通用样式头
// views/bar.ejs
<div class="bar">
<div>
<a href="/" class="home">
<img style="width:100%;height:100%" src="/images/index.jpg" alt="首页">
</a>
</div>
<div style="display: flex;width: auto;justify-content: space-around;">
<span style="margin-right:20px">您好:<%= username %></span>
<a style="margin-right:20px;color: white;" href="/write">写文章</a>
<a style="margin-right:20px;color: white;" href="/users/logout">退出</a>
</div>
</div>
<style>
.bar {
display: flex;
justify-content: space-between;
color: white;
background: #00B7FF;
height: 40px;
line-height: 40px;
padding: 10px 20px;
font-size: 18px;
}
.home {
display: block;
width: 40px;
height: 40px;
}
</style>
将其引入index.ejs文件中
// views/index.ejs
<!DOCTYPE html>
<html>
<head>
<title>首页</title>
<%- include head %>
</head>
<body>
<%- include bar %>
</body>
</html>
创建写文章页面write.ejs
// views/write.ejs
<!DOCTYPE html>
<html>
<head>
<title>写文章</title>
<%- include head %>
</head>
<body>
<%- include bar %>
<div class="container">
<form action="/article/add" method="post">
<input type="hidden" name="id" value="<%= currentArticle.id %>">
<input type="hidden" name="pageIndex" value="<%= currentArticle.pageIndex %>">
<input class="write-title margin-b-20" name="title" type="text" placeholder="请输入文章标题" value="<%= currentArticle.title %>">
<textarea class="write-content margin-b-20" name="content" rows="20"><%= currentArticle.content %></textarea>
<% if (currentArticle.id) { %>
<button class="button margin-b-20 flex" type="submit">更新</button>
<% } else { %>
<button class="button margin-b-20 flex" type="submit">发布</button>
<% } %>
</form>
</div>
</body>
<style>
.write-title {
width: 994px;
}
.write-content {
width: 994px;
padding: 10px 15px;
}
</style>
</html>
处理写文章write页面的路由方法
// routes/index.js
//渲染写文章页面
router.get('/write', (req, res) => {
let username = req.session.username;
var id = parseInt(req.query.id);
var pageIndex = req.query.pageIndex;
var currentArticle = {
title: '',
content: ''
}
if (id) {
model.connect((db) => {
db.collection('article').findOne({id: id}, (err, docs) => {
if (err) {
console.log('文章查找失败');
} else {
currentArticle = docs;
currentArticle['pageIndex'] = pageIndex;
res.render('write', {username: username, currentArticle: currentArticle});
}
})
})
} else {
res.render('write', {username: username, currentArticle: currentArticle});
}
})
routes文件夹下新建article.js文件并添加新增文章方法,由于之后有编辑操作,所以先将其一起写入,根据判断条件为跳转时的数据中是否存在id
// routes/article.js
//文章 添加/更新 操作
router.post('/add', (req, res) => {
var id = parseInt(req.body.id);
var pageIndex = req.body.pageIndex;
if (id) {
var title = req.body.title;
var content = req.body.content;
model.connect((db) => {
db.collection('article').updateOne({id: id}, {$set:{title: title, content: content}}, (err, docs) => {
if (err) {
console.log('文章更新失败' + err);
} else {
console.log('文章更新成功');
res.redirect('/?pageIndex=' + pageIndex);
}
})
})
} else {
let data = {
id: Date.now(),
username: req.session.username,
title: req.body.title,
content: req.body.content
}
model.connect((db) => {
db.collection('article').insertOne(data, (err, ret) => {
if (err) {
console.log('文章发布失败');
} else {
console.log('文章发布成功');
res.redirect('/');
}
})
})
}
})
routes文件夹下index.js获取文章列表数据
这里使用了一个新的模块 moment将数字串转换为时间格式
npm install moment -S 并将其引入
// routes/index.js
var moment = require('moment');
router.get('/', function(req, res) {
let username = req.session.username;
var data = {
pageIndex: req.query.pageIndex || 1,
pageTotal: 0,
pageSize: 3,
list: []
}
model.connect((db) => {
db.collection('article').find().toArray((err, docs) => {
if (err) {
console.log('获取文章列表失败');
} else {
data.pageTotal = Math.ceil(docs.length/data.pageSize);
//查询当前页的文章列表
model.connect((db) => {
db.collection('article').find().sort({_id: -1}).limit(data.pageSize).skip((data.pageIndex-1)*data.pageSize).toArray((err, docs2) => {
if (err) {
console.log('获取文章列表分页数据失败');
} else {
if (docs2.length === 0) {
res.redirect('/?pageIndex='+ ((data.pageIndex-1) || 1))
} else {
data.list = docs2;
data.list.map((item) => {
item['time'] = moment(item.id).format('YYYY-MM-DD HH:mm:ss');
})
res.render('index', { username: username, data: data });
}
}
})
})
}
})
})
});
index.ejs页面渲染文章列表
// views/index.ejs
<!DOCTYPE html>
<html>
<head>
<title>首页</title>
<%- include head %>
</head>
<body>
<%- include bar %>
<div class="container">
<table>
<tr>
<th>ID</th>
<th>作者</th>
<th>发布时间</th>
<th>标题</th>
<th>操作</th>
</tr>
<% data.list.map((item, index) => { %>
<tr>
<td><%= item._id %></td>
<td><%= item.username %></td>
<td><%= item.time %></td>
<td><a href="/article-detail?id=<%= item.id %>&pageIndex=<%= data.pageIndex %>"><%= item.title %></a></td>
<td><a style="margin-right:20px" href="/write?id=<%= item.id %>&pageIndex=<%= data.pageIndex %>">编辑</a><a href="/article/delete?id=<%= item.id %>&pageIndex=<%= data.pageIndex %>">删除</a></td>
</tr>
<% }) %>
</table>
<div class="pages">
<% for(let i = 1; i <= data.pageTotal; i++) { %>
<a href="/?pageIndex=<%= i %>"><%= i %></a>
<% } %>
</div>
</div>
</body>
<style>
.pages {
display: flex;
justify-content: flex-end;
}
</style>
</html>
文章删除
在article.js文件中编辑文章删除方法
// routes/article
//文章删除操作
router.get('/delete', (req, res) => {
var id = parseInt(req.query.id);
var pageIndex = req.query.pageIndex;
model.connect((db) => {
db.collection('article').deleteOne({id: id}, (err, ret) => {
if (err) {
console.log('文章删除失败');
} else {
console.log('文章删除成功');
}
res.redirect('/?pageIndex=' + pageIndex);
})
})
})
至此,此项目注册登录,以及数据的增删查改就已经完成了,不明白或者运行有问题的话可以去clone一下我上传到git上面完整的代码看看,也可以留言,感谢查看