Skip to content

anbang/zhuanbangBlog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

#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://localhost:3000/users/login

http://localhost:3000/articles/add

5、开发准备工作 ,把首页,登录页,注册页,发表文章页面做好 git版本存档

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%>

下面是这里的样子;

注册页面如下

登录页面如下

发表文章页面如下

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中的数据如图

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

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',{});
});

这个时候,么有登录时候显示登录和注册;如图

登录后显示发表文章和退出;

但是上面的有一个问题,即时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){
%>
<div class="alert alert-success" role="alert"><%=success %></div>
<%
 }else if(error){
%>
<div class="alert alert-danger" role="alert"><%=error %></div>
<%
}
%>

然后再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');
});

图像如下;

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

#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<articles.length;i++){
var article=articles[i];
%>
<div class="media">
  <div class="media-left">
    <a href="#">
      < img src="http://gravatar.duoshuo.com/avatar/e069fb6ca153e3c272bc85beb6f85b49?s=50
      " alt=""/>
      <%=article.user.username%>
    </a>
  </div>
  <div class="media-body">
    <h4 class="media-heading"><%=article.title%></h4>
    <p><%=article.content %></p>
  </div>
</div>
  <%
  }
  %>

最后的状态如下

没有登录看到的是

登录后看到的是

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

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

<div class="form-group">
    <label for="content" class="col-sm-2 control-label" > 图片</label>
    <input type="file" name="poster"/>
</div>

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

<form action="/articles/add" method="post" class="form-horizontal" enctype="multipart/form-data">

接收用户上传的中间件用的是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){
        %>
        <p><img src="<%=article.poster%>"  style="width: 200px;height: 200px" alt=""/></p>
        <%
     }
      %>

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

poster:String,

#11、添加详情页

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

<a href="/articles/detail/<%=article._id %>"><%=article.title%></a>

然后再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});
})
});

最终的详情页效果图如下

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

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

<div class="footer">
    <a href="/articles/edit/<%=article._id %>" class="btn btn-warning">编辑</a>
    <a href="/articles/delete/<%=article._id %>" class="btn btn-danger">删除</a>
</div>

然后删除的是在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,否则就把整个数据库里的文章给删除了;

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

然后是删除;顺手用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

    <!--下面是搜索信息-->
<form class="navbar-form navbar-left" role="search" method="get" action="/articles/list/1/2">
    <div class="form-group">
        <input type="text" name="keyword" value="<%=keyword%>" class="form-control" placeholder="搜索关键字">
    </div>
    <button type="submit" class="btn btn-default">搜索</button>
</form>

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添加文件

<nav class="container">
<ul class="pagination">
    <%
     if(pageNum>1){
         %>
        <li class="<%=pageNum==1?'disabled':''%>">
            <a href="/articles/list/<%=pageNum-1%>/2?keyword=<%=keyword %>" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
            </a>
        </li>
        <%
     }
    %>

    <%
     for (var i=1;i<=totalPage;i++){
         %><li class="<%=pageNum==i?'active':''%>"><a href="/articles/list/<%=i%>/2?keyword=<%=keyword %>"><%=i%></a></li>
    <%
     }
      %>

    <%
     if(pageNum<totalPage){
         %>
        <li class="<%=pageNum==totalPage?'disabled':''%>">
            <a href="/articles/list/<%=pageNum+1%>/2?keyword=<%=keyword %>" aria-label="Next">
                <span aria-hidden="true">&raquo;</span>
            </a>
        </li>
        <%
     }
  %>
</ul>
</nav>

最终效果如图

评论的如下

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published