项目安装
- 安装express脚手架:
npm install express-generator -g
- 创建项目:
express -e project
-e代表使用ejs模板,project是项目名称 - 进入项目:
npm install
下载依赖包 - 安装nodemon:
npm install nodemon -g
使用nodemon来启动项目,不用node来启动
- 启动项目:
npm start
,端口号在www启动文件中可以看。
项目连接MongoDB数据库
-
安装集成与nodejs的mongodb:
npm install mongodb -S
-
在项目中创建model文件,在下面创建index.js,所有和数据库连接的代码都封装在这个文件下
const MongoClient = require('mongodb').MongoClient; // 创建Mongo的客户端对象 const url = 'mongodb://localhost:27017'; // 连接数据库url const dbName = 'project'; // 连接的数据库名字 // 数据库的连接方法封装 function connect(callback){ // 使用connect方法连接到服务器 // err:错误对象。client:客户端连接成功的对象 MongoClient.connect(url, function(err, client) { if(err){ // 如果连接错误,打印错误信息 console.log('数据库连接错误!',err); }else{ // 否则 const db = client.db(dbName); // 数据库连接成功的对象 callback && callback(db); // 利用回调函数处理 client.close(); // 每次调用成功,还要再关闭数据库 } }); } module.exports = {connect}
-
调用:通过路由向数据库写入数据会用到
var express = require('express'); var router = express.Router(); var model = require('../model'); // 引入连接数据库方法 /* GET users listing. */ router.get('/', function(req, res, next) { res.send('respond with a resource'); }); // 注册接口 router.post('/regist',function(req, res, next){ var data = { username:req.body.username, password:req.body.password, password2:req.body.password2 } model.connect(function(db){ db.collection('users').insert(data,function(err,ret){ if(err){ console.log('注册失败!',err); res.redirect('/regist'); }else{ res.redirect('/login'); } }) }) }) module.exports = router;
项目引入MongoDB后的操作数据库语法
-
注册时,向数据库中写入用户注册的数据
model.connect(function(db){ // 这里的data是form表单提交过来的数据 db.collection('users').insert(data,function(err,ret){ if(err){ console.log('注册失败!',err); res.redirect('/regist'); }else{ res.redirect('/login'); } }) })
-
登陆时,在数据库中查找对应的数据,看看是否和数据库中一致
model.connect(function(db){ // 连接数据库 // 进入users库,寻找data这个数据.toArray转换成数组 db.collection('users').find(data).toArray(function(err,docs){ // 如果报错,重新登陆 if(err){ res.redirect('/login'); }else{ // 否则校验数据,如果是空数组,说明没找到这个用户,让他去重新登录.找到的话.登陆成功,进入主页面 if(docs.length > 0){ // 登陆成功,进行session会话存储(将用户的信息进行存储) // 这样的话,前端每次去请求的时候,先去session里面找username,如果有值,那么证明他是登陆状态的,那么直接跳转登陆页面就好 req.session.username = data.username; res.redirect('/'); // 进入主页 }else{ res.redirect('/login'); } } })
})
登录拦截(登陆成功后,会保存下用户的信息,在指定的时间里,用户再登录就不需要输入帐号和密码了)
-
在这里用到了一个工具模块
express-session
----服务端就是要通过session来保存 -
安装:
npm install express-session -S
-
配置session第一步:在app.js中引入
var session = require('express-session');
-
配置session第二步:在app.js中配置session中间件
// session配置 app.use(session({ secret: 'wangrenke project', // 可以随便改的 resave: false, saveUninitialized: true, cookie: { maxAge: 1000 * 60 * 5 } // 在服务端使用session时,会向客户端写入cookie,所以这里要指定一个cookie的有效期(即用户登陆多久是有效的)这里是五分钟 }))
-
使用:在用户登陆成功的时候,要去做一个会话的存储,将用户用存进session中,这样我们前端再请求的时候,先去seesion中取username,如果有值,说明是登陆状态,直接跳过登陆页面,否则,跳转登陆页面重新登陆
扫描二维码关注公众号,回复: 13085275 查看本文章model.connect(function(db){ // 连接数据库 // 进入users库,寻找data这个数据.toArray转换成数组 db.collection('users').find(data).toArray(function(err,docs){ // 如果报错,重新登陆 if(err){ res.redirect('/login'); }else{ // 否则校验数据,如果是空数组,说明没找到这个用户,让他去重新注册.找到的话.登陆成功,进入主页面 if(docs.length > 0){ // 登陆成功,进行session会话存储(将用户的信息进行存储) // 这样的话,前端每次去请求的时候,先去session里面找username,如果有值,那么证明他是登陆状态的,那么直接跳转登陆页面就好 req.session.username = data.username; res.redirect('/'); // 进入主页 }else{ res.redirect('/login'); } } }) })
-
设置登录拦截
// 登录拦截(当进入系统的时候) app.get('*',function(req,res,next){ var user = req.session.username; var path = req.path; console.log("session----user",user); // 如果是进的登录页或注册页,我们不拦截 if(path != '/login' && path != '/regist'){ if(!user){ res.redirect('/login'); } } next(); })
退出登录
-
清除session中的人员信息
-
跳转到登陆页面
// 退出登录 router.get('/loginout',function(req,res,next){ req.session.username = null; res.redirect('/login'); })
增删改查(写文章发布功能)----增
-
写文章时需要用到富文本框插件xhEditor:
http://xheditor.com/download
-
下载xhEditor后,将xheditor文件夹放在我们项目中,pulice下。
-
使用:在相应html 文件中引入依赖的文件并且初始化xheditor
<script type="text/javascript" src="/xheditor/jquery/jquery-1.4.4.min.js"></script> <script type="text/javascript" src="/xheditor/xheditor-1.2.2.min.js"></script> <script type="text/javascript" src="/xheditor/xheditor_lang/zh-cn.js"></script> <script> // 其实在本项目中基本上用前两个就行了 $('#elm1').xheditor({ tools: 'full', skin: 'default', showBlocktag: true, internalScript: false, internalStyle: false, width: 300, height: 200, loadCSS: 'http://xheditor.com/test.css', fullscreen: true, sourceMode: true, forcePtag: true, upImgUrl: "upload.php", upImgExt: "jpg,jpeg,gif,png" });
-
在textarea元素上加一个
class='xheditor'
,这样刷新页面就可以看到效果了
-
新建一个专门处理写文章的路由article.js。然后在app.js中配置article:
var articleRouter = require('./routes/article');app.use('/article', articleRouter);
-
写文章提交保存时,我们需要在mongodb数据库中,添加一条保存文章的数据。让我们提交保存时,能够存到数据库中。
文章列表实现
-
文章新增----写文章实现后,保存到了mongodb中的数据库中。保存后肯定要跳转到主页,那么主页一定有一个文章列表。那么我们在渲染主页页面路由的时候。操作mongodb,从对应的列表数据中取出来。展示到页面上
router.get('/', function(req, res, next) { var username = req.session.username; model.connect(function(db){ // 从库中,将articles文章张列表的数据。取出来转换成数组 db.collection('articles').find().toArray(function(err,docs){ console.log('文章列表------',docs); var list = docs; // 文章列表,用来传到index.ejs中 res.render('index', { msg: '首页',username:username,list:list }); }); }) });
-
我们存到库中的时间是毫秒。取出来也是,所以要使用一个插件用来转换时间:
npm install moment -S
-
使用
moment
:在需要用的文件引入var moment = require("moment");
model.connect(function(db){ // 从库中,将articles文章张列表的数据。取出来转换成数组 db.collection('articles').find().toArray(function(err,docs){ console.log('文章列表------',docs); var list = docs; // 文章列表,用来传到index.ejs中 list.map(function(item){ item.time = moment(item.id).format("YYYY-MM-DD hh:mm:ss"); return item; }) res.render('index', { msg: '首页',username:username,list:list }); }); })
分页查询
-
js部分
router.get('/', function(req, res, next) { var username = req.session.username || ''; // 当前页 var pageIndex = req.query.pageIndex || 1; // 分页 var data = { total: 0, // 文章总共的页数 curPage: pageIndex, // 当前页 list: [], // 当前页的文章列表 } var pageSize = 10; // 每次请求10条数据 model.connect(function(db){ // 第一步:查询所有文章(从库中,将articles文章张列表的数据。取出来转换成数组) db.collection('articles').find().toArray(function(err,docs){ // 文章列表总条数/每页显示的条数,向上取整。得到一共有多少页。 data.total = Math.ceil(docs.length / pageSize); // 第二步:查询当前页的文章列表(分页查询) model.connect(function(db){ // 重点:sort({_id:-1})表示倒序查询;limit(pageSize)表示限制多少条;skip((pageIndex - 1)*pageSize)从多少条开始查 db.collection('articles').find().sort({_id:-1}).limit(pageSize).skip((pageIndex - 1)*pageSize).toArray(function(err,doc2){ // 这里考虑到如果删除的时候,当前页只剩一条数据,那么删完之后,需要把对应的页签也删掉。 if(doc2.length == 0){ res.redirect('/?pageIndex='+((pageIndex-1) || 1)); return; } doc2.map(function(item){ item.time = moment(item.id).format("YYYY-MM-DD hh:mm:ss"); return item; }) data.list = doc2; res.render('index', { msg: '首页',username:username,data:data }); }) }) }); }) });
-
ejs部分
<!-- 分页 --> <div class="page"> <span>共<%= data.list.length %>条</span> <a class="top">上一页</a> <% for(var i = 1; i<=data.total; i++){ %> <a href="/?pageIndex=<% i %>" class="pages"><%= i %></a> <% } %> <a class="next">下一页</a> </div>
删除文章
// ejs页面
<a href="/article/delete?id=<%= item.id %>&pageIndex=<%= data.curPage %>">删除</a>
// /delete路由代码
// 删除
router.get('/delete',function(req,res,next){
var id = parseInt(req.query.id); // 页面传过来的需要删除的id
var pageIndex = req.query.pageIndex; // 页面传过来的当前页
model.connect(function(db){
// 删除对应id的数据deleteOne()
db.collection('articles').deleteOne({id: id},function(err,ret){
if(err){
console.log('删除失败!!');
}else{
console.log('删除成功!!');
}
// 删除完成后接着跳转对应的页面路由,这里有一个问题,就是如果删除时只剩一条数据,那么删掉之后需要把那一页的分页也删掉。这个限制在主体路由上。
res.redirect('/?pageIndex='+pageIndex);
})
})
})
修改文章
-
点击编辑时,页面和新增共用一个页面
-
修改时:和新增共用一个接口,同一个数据路集合
// 渲染写文章页面 || 编辑文章页面 router.get('/write',function(req,res,next){ var username = req.session.username; var id = parseInt(req.query.id); var pageIndex = req.query.pageIndex; var item = { title:'', // 标题 content:'' // 内容 } if(id){ // 如果有id,那么就是编辑 model.connect(function(db){ db.collection('articles').findOne({id:id},function(err,docs){ if(err){ console.log('查询失败!!!!'); }else{ item = docs item.pageIndex = pageIndex; console.log('aaaaaaa-------',item); res.render('write',{msg:'编辑文章',username:username,item:item}); } }) }) }else{ // 否则就是新增 res.render('write',{msg:'写文章',username:username,item:item}); } }) /* 新增 || 编辑 */ router.post('/add', function(req, res, next) { var id = parseInt(req.body.id); if(id){ // 编辑 var pageIndex = req.body.pageIndex; var title = req.body.title; var content = req.body.content; model.connect(function(db){ db.collection('articles').updateOne({id:id},{$set:{title:title,content:content}},function(err,ret){ if(err){ console.log('文章修改失败!',err); }else{ console.log('文章修改成功!'); res.redirect('/?pageIndex='+pageIndex); } }) }) }else{ // 新增 var data = { title: req.body.title, // 标题 content: req.body.content, // 内容 id: Date.now(), // 时间戳(什么时候提交的) username: req.session.username // 提交的用户(是谁写的) } model.connect(function(db){ db.collection('articles').insert(data,function(err,ret){ if(err){ console.log('文章发布失败!',err); res.redirect('/write'); }else{ res.redirect('/'); } }) }) } });
文章详情页(点击标题可以查看详情)
-
基本逻辑同编辑一样
-
新建一个详情页
<td><a href="/detail?id=<%= item.id %>"><%= item.title %></a></td> --------------------------------------------------- // 查看文章详情页面 router.get('/detail',function(req,res,next){ var username = req.session.username; var id = parseInt(req.query.id); var item = { title:'', // 标题 username:'', // 作者 id:'', // 时间&&id content:'', // 内容 } model.connect(function(db){ db.collection('articles').findOne({id:id},function(err,docs){ if(err){ console.log('查询失败!!!!'); res.redirect('/'); }else{ item = docs; item.time = moment(item.id).format('YYYY-MM-DD hh:mm:ss'); res.render('detail',{msg:'详情页',username:username,item:item}); } }) }) })
文件上传(也是通过富文本插件xheditor)
-
配置参数
-
在服务端定义
/upload路由
-
处理文件上传需要用到第三方插件
multiparty
,上传固定格式必须要是multiparty/form-data
-
下载:
npm install multiparty -S
,引入:var multiparty = require('multiparty');
,实例化:var form = new multiparty.Form()
实例化后就可以通过form对象,来解析请求体,把请求体中的文件解析处理。得到我们想要的结果;// 文件上传 router.post('/upload',function(req,res,next){ var form = new multiparty.Form(); form.parse(req,function(err,fields,files){ if(err){ console.log('上传失败呗!!!'); }else{ console.log('文件列表----',files); // 这个就是拿到的上传的文件数据,我们需要使用fs管道流的方式,写入到硬盘 var file = files.filedata[0]; var newPath = '/uploads/'+file.originalFilename; // 写入的路径加文件名字 var rs = fs.createReadStream(file.path); // 第一个流(读取的)--读的时候,就读带的路径就行 var ws = fs.createWriteStream("./public"+newPath); // 第二个流(写入的)--写的时候要写在服务端-这里放在pulice下的uploads中 rs.pipe(ws); // 当写入完成的时候,监听一个on('close')事件 ws.on('close',function(){ console.log('文件上传成功!!!'); res.send({err:'',msg:newPath}); }) } }) })
项目总结
集合了注册、登录、登录拦截、会话存储、分页、结合mongodb增删改查