Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/anbang/zhuanbangblog


https://github.com/anbang/zhuanbangblog

Last synced: about 8 hours ago
JSON representation

Awesome Lists containing this project

README

        

#this is my blog by node 【我的 node博客项目】

## express 用的是老版本的;
- 开始是用 npm install -g express-generator来安装用的;但是每次用的时候,出现不是可用的命令行;折腾半天没有解决;我就用老版本了
- 我用了;npm install -g [email protected] 这个版本来操作的;
- 后来解决好了,可以用npm install -g express-generator了

# 1、初始化一个仓库

- express -e zhuanbangBlog
- cd zhuanbangBlog && npm install
- 设置环境变量并启动服务器,在命令行中执行下列命令
- SET DEBUG=zhuanbangBlog:* & npm start
- 然后会显示zhuanbangBlog:server Listening on port 3000 +0ms
- 在浏览器里访问 http://localhost:3000 就可以显示欢迎页面
- Express
- Welcome to Express
- 再命令行输入 SET PORT=5000 可以把默认的3000端口改为5000
- 通过命令行简历一个忽略文件。touch .gitignore 忽略
- node_modules
- .idea
- 上面就是用express生成器生成了一个使用ejs模板的示例工程。

### 提交本地仓库
- git init 初始化git仓库
- git add -A 把所有的文件添加到暂存区
- git commit -m"初始化博客" 把所有的修改添加到历史区

### git 仓库关联github
- git remote add origin XXXXX.git 添加远程仓库的关联
- git push -u origin master 把本地的仓库推送到远程服务器上去

#2、生成文件说明
- app.js:express的主配置文件
- package.json:存储着工程的信息及模块依赖,当在 dependencies 中添加依赖的模块时,运行 npm install,npm 会检查当前目录下的 package.json,并自动安装所有指定的模块
- node_modules:存放 package.json 中安装的模块,当你在 package.json 添加依赖的模块并安装后,存放在这个文件夹下
- public:存放 image、css、js 等文件
- routes:存放路由文件
- views:存放视图文件或者说模版文件
- bin:可执行文件,可以从此启动服务器的

#3、功能分析
- 搭建一个简单的具有多人注册、登录、发表文章、登出功能的博客。

##设计目标
- 未登录:主页导航显示 首页、注册、登陆,下面显示已发表的文章、发表日期及作者。
- 登陆后:主页导航显示 首页、发表文章、退出,下面显示已发表的文章、发表日期及作者。
- 用户登录、注册、发表成功以及登出后都返回到主页。

# 4、路由规划
我们已经把设计的构想图贴出来了,接下来的任务就是完成路由规划了。路由规划,或者说控制器规划是整个网站的骨架部分,因为它处于整个架构的枢纽位置,相当于各个接口之间的粘合剂,所以应该优先考虑。

- / :首页
- /users/login :用户登录
- /users/reg :用户注册
- /articles/post :发表文章
- /articles/logout :登出

app.js中有用的核心代码

- app.use('/', routes);//根目录的路由
- app.use('/users', users);//用户的路由的目录文件用user.js来控制
- app.use('/articles', articles);//视图中的articles文件夹用articles来控制;

http://localhost:3000/users/reg

![](http://i.imgur.com/Hounhie.png)

http://localhost:3000/users/login

![](http://i.imgur.com/dnWDZm7.png)

http://localhost:3000/articles/add

![](http://i.imgur.com/LGEzIus.png)

# 5、开发准备工作 ,把首页,登录页,注册页,发表文章页面做好 [git版本存档](https://github.com/Broszhu/zhuanbangBlog/commit/3338397799c6452535c26c49101fb667d996aaaf)
MD5加密算法

- var crypto = require('crypto');
- var content = 'password'
- var md5 = crypto.createHash('md5');
- md5.update(content);
- var d = md5.digest('hex');

SHA1加密例程

- var crypto = require('crypto');
- var content = 'password'
- var shasum = crypto.createHash('sha1');
- shasum.update(content);
- var d = shasum.digest('hex');

### 配置bower
执行bower init

一路回车就可以生产一个bower.json 文件;然后再通过

- touch .bowerrc

创建一个.bowerrc文件;里面的内容输入

- {"directory":"./public/lib"}

这表示以后bower安装的模块都安装在./public/lib下面。

然后通过 bower install bootstrap --save 安装bootstrap文件;因为家了--save。不但会安装bootstrap,还会安装依赖的jquery;

然后通过拼接文档的方式来拼index.ejs;内容如下

- <% include include/header.ejs%>
- < div class="container">
- 这是主页的内容哦!
- < /div>
- <% include include/footer.ejs%>

下面是这里的样子;

![](http://i.imgur.com/ltL6U3w.png)

注册页面如下

![](http://i.imgur.com/RxRrA5Z.png)

登录页面如下
![](http://i.imgur.com/pHfkHm1.png)

发表文章页面如下
![](http://i.imgur.com/LmpeCKk.png)

# 6,博客【注册】&&【登录】功能完善,链接mongodb数据库

### 链接数据库

安装mongodb模块到node_modules下面并把此配置添加到package.json文件中

- npm install mongoose --save

mogodb的安装启用;在D:\mongodb\data文件夹下

- 命令窗体中输入 mongod --dbpath=D:\Mongodb\data 按回车键
注: --dbpath后的值表示数据库文件的存储路径 而且后面的路径必须存在否则服务开启失败
借助mongoose工具mongoVUE来管理;

- 在根目录下新建一个db文件夹,并且新建一个index.js文件;

var mongoose=require('mongoose');
mongoose.connect('mongodb://127.0.0.1:27017/zhuanbangblog');
mongoose.model('User',new mongoose.Schema({
username:String,
password:String,
email:String
}));
global.Model=function(modName){
return mongoose.model(modName);//一个参数是获取model值;并且放在global用;可以直接在users.js里用
}

//model2个是定义,一个是取值;

global.Model=function(modName){
return mongoose.model(modName);//一个参数是获取model值;并且放在global用;可以直接在users.js里用
}

一个参数是获取model值;并且放在global用;可以直接在users.js里用;model2个值是定义,一个是取值;

在app.js里require('./db'); 引入一下db里面的index.js;./db和./db/index.db是一样的效果的;

检查require是否正确,可以按住ctrl点击文件,如果可以访问,说明是正确的,如果访问不了,说明不成功的;

###user.js里的登录如下;
router.post('/reg', function (req, res) {
var user=req.body;//获取用户提交过来的注册表单
new Model('User')(user).save(function(err,user){
if(err){
res.redirect('/users/reg')
}else{
res.redirect('/users/login')
}
})
});

###mongoVUE中的数据如图

![](http://i.imgur.com/f9LFaeF.png)

###登录的和注册套路一样;

router.post('/login', function (req, res) {
var user=req.body;//获得请求过来的数据;
//在数据库里,查询客户输入的信息;找到一个就可以返回了;
Model('User').findOne(user,function(err,user){
console.log('err');
if(user){
res.redirect('/')
}else{
res.redirect('/users/login')
}
})
});
如果登录成功就返回到首页,如果登录失败就留在登录页;

# 7 会话支持模块

导航条内做判断

app.js里装会话中间件

var session=require('express-session');//安装后导入会话中间节;

app.use(session({
secret: 'anbangblog',
resave: true,
saveUninitialized: true,
cookie:{
maxAge:60*1000*30
}
})
);

app.use(function(req,res,next){//把请求的user放到res.locals.user上,这样访问的页面就都能用了
res.locals.user=req.session.user;
next();
});

因为可以用app里面的res内容;所以下面users.js里的代码需要改变了;

router.get('/reg', function (req, res,next) {//登录
res.render('users/reg',{});
});

router.get('/login', function (req, res, next) {//注册
res.render('users/login',{});
});

这个时候,么有登录时候显示登录和注册;如图
![](http://i.imgur.com/VDQYs0V.png)

登录后显示发表文章和退出;
![](http://i.imgur.com/u7LRqwA.png)

但是上面的有一个问题,即时cookie设置是半小时过期;,就是重新启动服务器后,又需要登录了;相当于在一个店铺办卡,然后店铺倒闭,别人再这了重新开一个店铺,以前的卡在新开店里就不认识了;
这里需要用到一个包;connect-mongo;可以把session保存到数据库了;

安装后,然后app.js引入;

var MongoStore = require('connect-mongo')(session);//保持session在数据库里的中间件,重启服务器后session也不会丢失;

在app.js下面加入需要引入的数据库;

app.use(session({
secret: 'anbangblog',
resave: true,
saveUninitialized: true,
store: new MongoStore({//保存session的数据库保存地址;
url: 'mongodb://127.0.0.1:27017/zhuanbangblog'
}),
cookie:{
maxAge:60*1000*30
}
})
);

这样无论服务器重启如否都不需要再次登录了;

### 提示消息通知

很多时候注册表单错了,如果只提示错误,并不告诉错误原因,用户会很崩溃;通过connrct-flash可以来实现

npm install connect-flash --save
先安装flash包;
然后通过下面的代码引入到app.js里;

var flash = require('connect-flash');
app.use(flash());

user.js里添加

req.flash('success',"恭喜您,登录成功");//类似于req.session.success="登录成功"
如果失败了,用

req.flash('error',"滚粗,登录失败,回家种田去吧!");

在前台里面需要用<%=success%>和<%=error%>来供替换;

因为是消息提示的,所以需要用到ifelse来判断;

<%
if(success){
%>

<%=success %>

<%
}else if(error){
%>
<%=error %>

<%
}
%>

然后再app,js里添加东西;

原本是

app.use(function(req,res,next){
res.locals.user=req.session.user;
next();
});

添加后是

res.locals.success=req.flash("success").toString();//req.flash("success")取出来的是数组,需要toString一下;
res.locals.error=req.flash("error").toString();

因为req.flash("success")可以多次提示的,也属于一个数据,需要toString一下,才能让上面的判断生效;

#8 用户访问的权限控制
登录后的用户,不能访问注册和登录页面;
没有登录的时候不能访问退出页面;

需要新建一个中间件,在根目录下新建一个middleware文档。在里面新建一个index.js的中间件代码如下

//登录才能继续访问的
exports.checkLogin=function(req,res,next){
if(req.session.user){//您已经登录过了
next();//继续执行
}else{
req.flash('error','您还没有登录,需要重新登录');
res.redirect('back')
}
};

//未登录才能继续访问的
exports.checkNotLogin=function(req,res,next){
if(req.session.user){//您已经登录过了
req.flash('error','您已经登陆过了,不需要重复登录');
res.redirect('back')
}else{
next();//继续执行
}
};

然后再user中,在参数里假如,会先执行的里面的;添加的注册页面如下

router.get('/reg', middleware.checkNotLogin, function (req, res,next) {
res.render('users/reg',{});
});


退出界面如下

router.get('/logout', middleware.checkLogin, function (req, res) {
req.session.user = null;
req.flash('success',"退出成功,下次进来需要登录哦");
res.redirect('/users/login');
});

图像如下;

![](http://i.imgur.com/m9bZCNP.png)

![](http://i.imgur.com/si5Zwl3.png)

这样就做好了用户权限的控制;

#9、发表文章页面的制作;

在routes/articles里修改;套路和注册一样的;

router.post('/add', function (req, res, next) {
var article=req.body;
new Model('Article')(article).save(function(err,article){
if(err){
res.redirect('back')
}else{
res.redirect('/')
}
})
});

这个时候需要mongodb里改下model;因为现在的model没有Article,只有User的;在db下面的index.js里添加;

mongoose.model('Article',new mongoose.Schema({
title:String,
content:String
}));

这个时候就做好了发表文档的页面;发表后可以在mongoVUE里查看到zhuanbangblog的数据库下面的Collections下面多了一个articles的数据表

#首页显示文章的列表

首先在route/index.js中添加文件,把文章给读取出来

router.get('/', function(req, res) {//当用户访问根目录;也就是 / 的时候执行此回调
var article = req.body;
//下面的populate('user')是mongo提供的方法;会找到user,然后循环name,id等;把用户的ID转成对象;这个用法非常好用,一定要记得用;
Model('Article').find({}).populate('user').exec(function(err,articles){
res.render('index', { articles: articles});
});
});

然后需要修改db文件下面的index.js

mongoose.model('Article',new mongoose.Schema({
title:String,
content:String,
user:{type:ObjectId,ref:'User'}//对象ID类型引用User;加的是这一条;ref引用的是上面定好的User
}));

其中的ObjectId需要重新的指定;在开始的部分需要下一段下面代码

var ObjectId=mongoose.Schema.Types.ObjectId;//Types相当于枚举

然后就是需要重头戏了,需要改routes/srticle.js文件

router.post('/add', function (req, res, next) {
var article=req.body;
article.user = req.session.user._id;//给article赋值用户的ID;
new Model('Article')(article).save(function(err,article){
if(err){
res.redirect('back')
}else{
res.redirect('/')
}
})
});

然后就是渲染模板了;在views/index.ejs改的代码如下

<%
for(var i=0;i


<%
}
%>

最后的状态如下

没有登录看到的是
![](http://i.imgur.com/E8dIx0P.png)

登录后看到的是

![](http://i.imgur.com/lnTailQ.png)

# 10、文章里可以插入图片,并且首页显示

首先是在add文章添加页面把原型弄好;在一个file文件


图片


这时候需要注意:form里面需要指定:enctype="multipart/form-data"意思的不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。form开始的代码如下

接收用户上传的中间件用的是multer;在app.js里引入

var multer=require('multer');

安装的时候记得保存;用的代码是

npm install multer --save

中间件的变化一般都是非常大的,用的时候直接看中间件的reademe里面的用法就可以了;

在articles.js里添加如下文件

var path=require('path');
var multer=require('multer');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '../public/upload')
},
filename: function (req, file, cb) {
cb(null, Date.now()+'.'+path.extname(file.originalname));
}
})

备注:上面代码是../public/upload是文件存储的位置,这样储存不用在键路由了;文件名用的是当前时间戳Date.now()加上原名字的后缀;用的path.extname来取文件的后缀;中间用点来链接;(后来亲测不用.来链接可以的;中间点那个字符串可以去掉)

在下面的router.post('/add', function (req, res, next) {改成

router.post('/add', upload.single('poster') , function (req, res, next) {

里面添加poster的位置

article.poster=path.join('/upload',req.file.filename);

在views/index.ejs里修改首页显示的视图

<%
if(article.poster){
%>


<%
}
%>

db下面的index里面需要修改一下Acticle的属性加一个

poster:String,

![](http://i.imgur.com/IWtOgLt.png)

#11、添加详情页

首页的views/index.ejs里文件标题修改

<%=article.title%>

然后再routes/articles.js下面加一个路由

router.get('/detail/:id',function(req,res){
var id = req.params.id;
Model('Article').findById(id,function(err,article){
res.render('articles/detail',{article:article});
})
});

最终的详情页效果图如下

![](http://i.imgur.com/FfJoOBy.png)

### 增加详情页的【编辑】【功能】;

在detail下面加一个删除的代码

然后删除的是在routes/articles下面加一个删除的路由

router.get('/delete/:id',function(req,res){
var id = req.params.id;
Model('Article').remove({_id:id},function(err){
res.redirect('/');
})
});

需要注意的是里面的{_id:id}这里要这么写,不能直接写成id,否则就把整个数据库里的文章给删除了;

这样删除就做好了,删除回到首页的;

![](http://i.imgur.com/7gnMQMr.png)

然后是删除;顺手用middleware.checkLogin做了权限判断;

###编辑功能;
编辑主要是改

router.post('/add' , upload.single('poster') ,middleware.checkLogin, function (req, res, next) {

改后的代码如下

router.post('/add' , upload.single('poster') ,middleware.checkLogin, function (req, res, next) {
var article=req.body;
var id=article.id;
if(id){
var updateObj = {
title:article.title,
content:article.content,
}
if(req.file){
var poster = path.join('/upload',req.file.filename);
updateObj.poster = poster;
}

new Model('Article').update({_id:id},{$set:updateObj},function(err){
if(err){
res.redirect('back');
}else{
res.redirect('/articles/detail/'+id);
}

});
}else{
article.user = req.session.user._id;//给article赋值用户的ID;
if(req.file){
article.poster=path.join('/upload',req.file.filename);
}
new Model('Article')(article).save(function(err,article){
if(err){
res.redirect('back')
}else{
res.redirect('/')
}
})
}
});

现在就做好了编辑文件;如果有图片,就用新图片,如果没有图片,就用原来的图片;如果发表的时候有图片,就用图片,如果没有,就直接标题和文字;

#12 搜索和分页

首先修改views下面的head.js






搜索

route下面的articles加一个路由

router.get('/list/:pageNum/:pageSize', function(req, res) {
var pageNum = parseInt(req.params.pageNum);
pageNum = pageNum<=0?1:pageNum;
var pageSize = parseInt(req.params.pageSize);
var keyword = req.query.keyword;
var query = new RegExp(keyword,"i");
Model('Article').count({$or:[{title:query},{content:query}]},function(err,count){
var totalPage = Math.ceil(count/pageSize);
pageNum = pageNum>=totalPage?totalPage:pageNum;
Model('Article').find({$or:[{title:query},{content:query}]})
.skip((pageNum-1)*pageSize).limit(pageSize).exec(function(err,articles){
res.render('index',{
title:'主页',
pageNum:pageNum,
pageSize:pageSize,
keyword: keyword,
totalPage:totalPage,
articles:articles
});
});
});

});

app.js里面加文件

app.use(function(req,res,next){
res.locals.keyword="";

这个是解决刚开始访问没有keyword时候的;

routes/index.js下面修改文件

res.redirect('/articles/list/1/2');

默认显示这里;

views下面的index.ejs添加文件



    <%
    if(pageNum>1){
    %>


  • «


  • <%
    }
    %>

    <%
    for (var i=1;i<=totalPage;i++){
    %>

  • <%=i%>

  • <%
    }
    %>

    <%
    if(pageNum



  • »


  • <%
    }
    %>

最终效果如图

![](http://i.imgur.com/3gbMRVe.png)

评论的如下

![](http://i.imgur.com/O7PcPFN.png)