项目安装
- 安装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,如果有值,说明是登陆状态,直接跳过登陆页面,否则,跳转登陆页面重新登陆
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增删改查
标签:function,username,req,err,Nodejs,MongoDB,Express,res,var From: https://blog.51cto.com/u_16264339/7479469