精华 使用express4.x版和Jade模板重写《nodejs开发指南》微博实例
发布于 11 年前 作者 tonyzhan 45954 次浏览 最后一次编辑是 9 年前 来自 分享

本实例已经将完整代码上传github了,网址 https://github.com/tonyzhan/microblog 欢迎大家下载!

《nodejs开发指南》是一本好书,但微博开发实例已经过时了。express4.x发布了,Jade模板是express的默认模板。 学习了jade模板后发觉也没什么难的,而且html代码清爽了许多。 网上好多人说ejs模板怎么好,我却觉得仿佛回到了asp时代,凡是像microsoft的东西,我都不感冒。 我使用express4.x版和Jade模板重写《nodejs开发指南》微博实例,请大家参考,欢迎大家多提意见。

Installation 安装

$ npm install express

快速建立项目

$ npm install -g express-generator

创建项目目录

$ express /tmp/microblog && cd /tmp/microblog

安装中间件和依赖项

$ npm install

启动服务器, 这个有些特别,请注意:

$ cd /tmp/microblog
$ bin/www

##我的目录,敬请参考

microblog
--bin
 --www
--models
 --db.js
 --post.js
 --user.js
--node_modules
 --body-parser
 --connect-flash
 --connect-mongo
 --cookie-parser
 --debug
 --express
 --express-session
 --jade
 --mongodb
 --morgan
 --static-favicon
--public
 --images
 --javascripts
 --bootstrap.js
 --jquery.js
 --stylesheets
 --bootstrap.css
 --bootstrap-responsive.css
 --style.css
--routes
 --index.js
 --users.js //这个是express自带的,基本没用
--views 
 --error.jade //这个是express自带的,基本没用
 --index.jade
 --layout.jade
 --login.jade
 --posts.jade
 --reg.jade
 --say.jade
 --user.jade //这个是ejs模板需要用到的,在jade模板下发觉没有用,可以使用include直接放在index.jade就可以实现需求了
--app.js
--package.json
--setting.js

以下是源代码,就不说废话了:

app.js的代码:

var express = require('express');
var path = require('path');
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
//var partials = require('express-partials');用jade模板,不能使用这个中间件
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
var settings = require('./settings');
var flash = require('connect-flash');
var routes = require('./routes/index');
var users = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
//app.use(partials());
app.use(favicon());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
//cookie解析的中间件
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(flash());
//提供session支持
app.use(session({
 secret: settings.cookieSecret,
 store: new MongoStore({
 db: settings.db,
 })
}));
app.use(function(req, res, next){
 console.log("app.usr local");
 res.locals.user = req.session.user;
 res.locals.post = req.session.post;
 var error = req.flash('error');
 res.locals.error = error.length ? error : null;
 
 var success = req.flash('success');
 res.locals.success = success.length ? success : null;
 next();
});
app.use('/', routes);
app.use('/users', users);
/// catch 404 and forward to error handler
app.use(function(req, res, next) {
 var err = new Error('Not Found');
 err.status = 404;
 next(err);
});
/// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
 app.use(function(err, req, res, next) {
 res.status(err.status || 500);
 res.render('error', {
 message: err.message,
 error: err
 });
 });
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
 res.status(err.status || 500);
 res.render('error', {
 message: err.message,
 error: {}
 });
});
module.exports = app;
## index.js代码
var express = require('express');
var app = express();
var router = express.Router();
var crypto = require('crypto');
var User = require('../models/user.js');
var Post = require("../models/post.js");
/* GET home page. */
router.get('/', function(req, res) {
 Post.get(null, function(err, posts) {
 if (err) {
 posts = [];
 }
 res.render('index', {
 title: '首页',
 posts: posts,
 user : req.session.user,
 success : req.flash('success').toString(),
 error : req.flash('error').toString()
 });
 });
 //res.render('index', { title: '首页' });
});
router.get("/reg", checkNotLogin);
router.get("/reg",function(req,res) {
 res.render("reg",{
 title : "用户注册"
 });
});
router.get("/login", checkNotLogin);
router.get("/login",function(req,res) {
 res.render("login",{
 title:"用户登录",
 });
});
router.get("/logout", checkLogin);
router.get("/logout",function(req,res) {
 req.session.user = null;
 req.flash('success', '退出成功');
 res.redirect('/');
});
router.get("/user", function(req,res){
 res.render("user",{
 title: "用户页面",
 });
});
router.post("/login", checkNotLogin);
router.post("/login",function(req,res) {
 var md5 = crypto.createHash('md5');
 var password = md5.update(req.body.password).digest('base64');
 User.get(req.body.username, function(err, user) {
 if (!user) {
 req.flash('error', '用户不存在');
 return res.redirect('/login');
 }
 
 if (user.password != password) {
 req.flash('error', '用户名或密码错误');
 return res.redirect('/login');
 }
 req.session.user = user;
 req.flash('success', req.session.user.name + '登录成功');
 
 res.redirect('/');
 });
});
router.post("/reg", checkNotLogin);
router.post("/reg", function(req, res) {
 console.log(req.body['password']);
 console.log(req.body['password-repeat']);
 if(req.body['password-repeat'] != req.body['password']){
 req.flash('error', '两次输入的密码不一致');
 return res.redirect('/reg');
 } 
 var md5 = crypto.createHash('md5');
 var password = md5.update(req.body.password).digest('base64');
 var newUser = new User({
 name: req.body.username,
 password: password,
 });
 //检查用户名是否已经存在
 User.get(newUser.name, function(err, user) {
 if (user) {
 err = 'Username already exists.';
 }
 if (err) {
 req.flash('error', err);
 return res.redirect('/reg');
 }
 newUser.save(function(err) {
 if (err) {
 req.flash('error', err);
 return res.redirect('/reg');
 }
 req.session.user = newUser;
 req.flash('success', req.session.user.name+'注册成功');
 res.redirect('/');
 });
 }); 
});
function checkNotLogin(req, res, next) {
 if (req.session.user) {
 req.flash('error', '用户已经登录');
 return res.redirect('/');
 }
 next();
}
function checkLogin(req, res, next) {
 if (!req.session.user) {
 req.flash('error', '用户尚未登录');
 return res.redirect('/login');
 }
 next();
}
router.post("/post",checkLogin);
router.post("/post",function(req,res) {
 var currentUser = req.session.user;
 var post = new Post(currentUser.name, req.body.post);
 post.save(function(err) {
 if (err) {
 req.flash('error', err);
 return res.redirect('/');
 }
 req.flash('success', '发表成功');
 res.redirect('/u/' + currentUser.name);
 });
});
router.get("/u/:user",function(req,res) {
 User.get(req.params.user, function(err, user) {
 if (!user) {
 req.flash('error', '用户不存在');
 return res.redirect('/');
 }
 Post.get(user.name, function(err, posts) {
 if (err) {
 req.flash('error', err);
 return res.redirect('/');
 }
 res.render('user', {
 title: user.name,
 posts: posts
 });
 });
 });
});
module.exports = router;

##settings.js

module.exports = {
cookieSecret: 'microblogtony2014',
db: 'blog',
host: 'localhost',
};

package.json:

express-partials不需要装,这是ejs模板需要用到了。

#我装了并引用,jade模板就不工作了,费了我好长的时间去找问题。

{
 "name": "microblog",
 "version": "0.0.1",
 "private": true,
 "scripts": {
 "start": "node ./bin/www"
 },
 "dependencies": {
 "express": "~4.2.0",
 "static-favicon": "~1.0.0",
 "morgan": "~1.0.0",
 "cookie-parser": "~1.0.1",
 "body-parser": "~1.0.0",
 "debug": "~0.7.4",
 "jade": "~1.3.0",
 "mongodb": ">= 1.4.8",
 "connect-mongo": ">=0.1.7",
 "express-session": "~1.0.4",
 "connect-flash": "*"
 }
}

##db.js

var settings = require('../settings'),
 Db = require('mongodb').Db,
 Connection = require('mongodb').Connection,
 Server = require('mongodb').Server;
module.exports = new Db(settings.db, new Server(settings.host, Connection.DEFAULT_PORT, {}), {safe: true});

##post.js

var mongodb = require('./db');
function Post(username,post,time) {
 this.user = username;
 this.post = post;
 if(time) {
 this.time = time;
 } else {
 this.time = new Date();
 }
};
module.exports = Post;
Post.prototype.save = function save(callback) {
 var post = {
 user: this.user,
 post: this.post,
 time: this.time,
 };
 mongodb.open(function(err, db) {
 if (err) {
 return callback(err);
 }
 db.collection('posts', function(err, collection) {
 if (err) {
 mongodb.close();
 return callback(err);
 }
 // collection.ensureIndex('user');
 collection.insert(post, {safe: true}, function(err, post) {
 mongodb.close();
 callback(err, post);
 });
 });
 });
};
Post.get = function get(username, callback) {
 mongodb.open(function(err, db) {
 if (err) {
 return callback(err);
 }
 db.collection('posts', function(err, collection) {
 if (err) {
 mongodb.close();
 return callback(err);
 }
 var query = {};
 if (username) {
 query.user = username;
 }
 collection.find(query).sort({time: -1}).toArray(function(err, docs) {
 mongodb.close();
 if (err) {
 callback(err, null);
 }
 var posts = [];
 docs.forEach(function(doc, index) {
 var post = new Post(doc.user, doc.post, doc.time);
 posts.push(post);
 });
 callback(null, posts);
 });
 });
 });
};

##user.js

var mongodb = require('./db');
function User(user) {
 this.name = user.name;
 this.password = user.password;
};
module.exports = User;
//存入Mongodb的文档
User.prototype.save = function save(callback) {
 var user = {
 name: this.name,
 password: this.password,
 };
 mongodb.open(function(err, db) {
 if (err) {
 return callback(err);
 }
 //读取users集合
 db.collection('users', function(err, collection) {
 if (err) {
 mongodb.close();
 return callback(err);
 }
 // 为name属性添加索引
 // collection.ensureIndex('name', {unique: true});
 //写入user文档
 collection.insert(user, {safe: true}, function(err, user) {
 mongodb.close();
 callback(err, user);
 });
 });
 });
}
User.get = function get(username, callback) {
 mongodb.open(function(err, db) {
 if (err) {
 return callback(err);
 }
 //读取users集合
 db.collection('users', function(err, collection) {
 if (err) {
 mongodb.close();
 return callback(err);
 }
 //查找name属性为username的文档
 collection.findOne({name: username}, function(err, doc) {
 mongodb.close();
 if (doc) {
 //封装文档为User对象
 var user = new User(doc);
 callback(err, user);
 } else {
 callback(err, null);
 }
 });
 });
 });
};

##layout.jade

doctype html
html
 head
 title= title
 link(rel='stylesheet', href='/stylesheets/bootstrap.css')
 link(rel='stylesheet', href='/stylesheets/style.css')
 link(rel='stylesheet', href='/stylesheets/bootstrap-responsive.css')
 script(src='/javascripts/jquery.js')
 script(src='/javascripts/bootstrap.js') 
 body
 div.navbar.navbar-fixed-top
 div.narbar-inner
 div.container
 a(class='btn btn-navbar' data-toggle='collapse' data-target='.nav-collapse') span.icon-bar span.icon-bar span.icon-bar
 a(class='brand' href='/') Microblog
 div.nav-collapse
 ul.nav
 li.active 
 a(href='/') 首页
 - if (!user)
 li
 a(href='/login') 登录
 li
 a(href='/reg') 注册
 - else
 li
 a(href='/logout') 退出
 div#container.container
 - if (success)
 div.alert.alert-success= success
 - if (error)
 div.alert.alert-error= error 
 block content
 div#footer.footer
 !='<hr />'
 p
 a(href='http://www.hotelanywhere.cn' target='_blank') Tony Zhang [@2014](/user/2014) All Right Reserved

##index.jade

extends layout
block content
 - if (!user)
 div.hero-unit
 h1 Welcome to Microblog
 p Microblog是一个基于Node.js的微博系统。
 p
 a(class='btn btn-primary btn-large' href='/login') 登录
 a(class='btn btn-large' href='/reg') 注册
 - else
 include ./say.jade
 include ./posts.jade

##reg.jade

extends layout
block content
 
 form(class='form-horizontal' method='post')
 fieldset
 legend 用户注册
 div.control-group
 label(class='control-label' for='username') 用户名
 div.controls
 input(type='text' class='input-xlarge' id='username' name='username')
 p(class='help-block') 你的账户名称,用于登录和显示
 div.control-group
 label(class='control-label' for='password') 密码
 div.controls
 input(type='password' class='input-xlarge' id='password' name='password')
 div.control-group
 label(class='control-label' for='password') 请再次输入密码
 div.controls
 input(type='password' class='input-xlarge' id='password-repeat' name='password-repeat')
 div.form-actions
 button(type='submit' class='btn btn-primary') 注册

##login.jade

extends layout
block content
 form( class='form-horizontal' method='post')
 fieldset
 legend 用户登录
 div.control-group
 label(class='control-label' for='username') 用户名
 div.controls
 input(type='text' class='input-xlarge' id='username' name='username')
 div.control-group
 label(class='control-label' for='username') 密码
 div.controls
 input(type='password' class='input-xlarge' id='password' name='password')
 div.form-actions
 button(type='submit' class='btn btn-primary') 登录

##say.jade

form( class='well form-inline center' method='post' action='/post' style='text-align:center;')
 input(type='text' class='span8' id='post' name='post')
 button(type='submit' class='btn btn-success') 发言

##posts.jade

- for (var i = 3; i < posts.length+3; i = i + 3)
 div.row
 - for(var j = i-3; j < i && j < posts.length; j++)
 div.span4
 h2
 a(href='/u/'+posts[j].user)=posts[j].user 
 !='说'
 p!='<small>' + posts[j].time + '</small>'
 p=posts[j].post

##user.jade: 以前我以为不需这个模板,后来才发现这个模板的重要,是查看每个用户的发言的必要的模板

extends layout
block content
 include narbar.jade
 div.jumbotron
 div.container
 - if (user)
 include ./say.jade
 div.container
 include ./posts.jade
 include footer.jade

##error.jade

extends layout
block content
 h1= message
 h2= error.status
 pre #{error.stack}
47 回复

这么好的资料,居然没人回复。 谢谢分享啦。

多谢jayself的鼓励! posts.jade做了更新,解决了之前没有做到的3个发言在一行的样式问题。由于jade的语法,我使用了2重循环解决了实现了div的嵌套。对比ejs模板,jade在有条件嵌套div上有些支持不够。 以下为posts.jade的代码,供大家参考。如果有更好的解决方法,请分享给我,多谢了。

  • for (var i = 3; i < posts.length+3; i = i + 3) div.row
    • for(var j = i-3; j < i && j < posts.length; j++) div.span4 h2 a(href=’/u/’+posts[j].user)=posts[j].user !=‘说’ p!=’<small>’ + posts[j].time + '</small>' p=posts[j].post

在回复里贴代码,小横线变成了小圆点,请大家复制代码时注意。

代码放在github上岂不是更好。。。

回复junxing, 已经把代码放在github了。但由于是新手,还没有搞清楚如何把图片和需要引用的jquery.js和bootstrap.js, 以及css文件上传到repositories,请赐教,谢谢!

本实例已经将完整代码上传github了,网址 https://github.com/tonyzhan/microblog 欢迎大家下载!

求QQ 一起交流一下

不错,正在找jade的项目,谢谢啦

我的QQ号是2411551649, 欢迎与我交流

不错,终于有份源码学学......

setttings.js

module.exports = {
cookieSecret: 'microblogtony2014',
db: 'blog',
host: '192.168.1.104',
};

配置已经改了,还是提示 Error: Error connecting to database: failed to connect to [127.0.0.1:27017]

TypeError: Property ‘next’ of object #<IncomingMessage> is not a function

回yakczh, 能把你的代码发给我吗? 我可以看看问题在哪。

@tonyzhan 没有改代码,就是改了配置文件,我本地没有mongodb,用的是局域网服务器上的mongodb你换个host试下

回yakczh," Error: Error connecting to database: failed to connect to [127.0.0.1:27017]"说明还是在连接本地的mongodb, 不是连接192.168.1.104. 你再查查局域网服务器的mangodb连接有没有设置IP限制。 实在不行,就在本地安装mangodb吧

@yakczh 阅读一下connect-mongo.js的构造方法 //提供session支持 app.use(session({ secret: settings.cookieSecret, store: new MongoStore({ db: settings.db, url: ‘mongodb://’+settings.host+’:27017/’ }) }));

@tonyzhan

执行/u/:user的方法时报错,我用console打出程序执行顺序的时候,发现已经执行到 res.render(‘user’, { title: user.name, posts: posts }); 请教一下问题出在哪里

http.js:689 throw new Error(‘Can’t set headers after they are sent.’); ^ Error: Can’t set headers after they are sent. at ServerResponse.OutgoingMessage.setHeader (http.js:689:11) at IncomingMessage.write (/root/Documents/proj/microblog/node_modules/express/node_modules/finalhandler/index.js:107:9) at IncomingMessage.g (events.js:180:16) at IncomingMessage.emit (events.js:117:20) at _stream_readable.js:943:16 at process._tickCallback (node.js:419:13)

还是得感谢楼主终于找到一个靠谱的express4+jade的教程了

谢谢这么无私的奉献

@jacobsuyu 这个问题已经解决,是由于user.jade模板的代码错误,请看我上面修改的代码,或者重新从github上下载我更新的代码。抱歉,最近很忙,没有登录cnode社区。

实在看不下去, 帮楼主整个标记了一遍...

clone嘞 嘞 謝謝

简直好东西啊 - -

好东西!慢慢学习!

真心不错,学习中

@cunzhi 可以改版了哇哈~~~

楼主,网页载入时出现以下错误: Error at new JS_Parse_Error (/Users/Janet/Node/microblog/node_modules/jade/node_modules/with/node_modules/uglify-js/lib/parse.js:189:18) at js_error (/Users/Janet/Node/microblog/node_modules/jade/node_modules/with/node_modules/uglify-js/lib/parse.js:197:11) at croak (/Users/Janet/Node/microblog/node_modules/jade/node_modules/with/node_modules/uglify-js/lib/parse.js:661:41) at token_error (/Users/Janet/Node/microblog/node_modules/jade/node_modules/with/node_modules/uglify-js/lib/parse.js:665:9) at unexpected (/Users/Janet/Node/microblog/node_modules/jade/node_modules/with/node_modules/uglify-js/lib/parse.js:671:9) at /Users/Janet/Node/microblog/node_modules/jade/node_modules/with/node_modules/uglify-js/lib/parse.js:831:17 at /Users/Janet/Node/microblog/node_modules/jade/node_modules/with/node_modules/uglify-js/lib/parse.js:704:24 at block_ (/Users/Janet/Node/microblog/node_modules/jade/node_modules/with/node_modules/uglify-js/lib/parse.js:978:20) at /Users/Janet/Node/microblog/node_modules/jade/node_modules/with/node_modules/uglify-js/lib/parse.js:951:25 at function_ (/Users/Janet/Node/microblog/node_modules/jade/node_modules/with/node_modules/uglify-js/lib/parse.js:956:15)

请问是什么样的原因造成的?望回复!mecil9@jiachongs.com qq:7187689

这个应该是Jade module 安装问题,请使用NPM install重新安装最新版本的jade

为什么会报

sunlifei[@sunlifei-V470](/user/sunlifei-V470):~/Documents/Learning Node/microblog$ NODE_ENV=production node cluster.js
/home/sunlifei/Documents/Learning Node/microblog/models/db.js:5
e.exports = new Db(settings.db, new Server(settings.host, Connection.DEFAULT_P
 ^
TypeError: Cannot read property 'DEFAULT_PORT' of undefined
 at Object.<anonymous> (/home/sunlifei/Documents/Learning Node/microblog/models/db.js:5:74)
 at Module._compile (module.js:460:26)
 at Object.Module._extensions..js (module.js:478:10)
 at Module.load (module.js:355:32)
 at Function.Module._load (module.js:310:12)
 at Module.require (module.js:365:17)
 at require (module.js:384:17)
 at Object.<anonymous> (/home/sunlifei/Documents/Learning Node/microblog/models/user.js:1:77)
 at Module._compile (module.js:460:26)
 at Object.Module._extensions..js (module.js:478:10)

@tonyzhan 跑不起来,指点下

可以将DEFAULT_PORT 改为mongodb的端口,如 ‘27017’

@tonyzhan 你这个项目是启动 cluster.js ?

QQ截图20150601115845.jpg

我问下怎么运行呢?我是在windows环境下!

大神,我下载下来运行报错了. D:\nodejs\microblog-master>node app D:\nodejs\microblog-master\models\db.js:5 e.exports = new Db(settings.db, new Server(settings.host, Connection.DEFAULT_P ^ TypeError: Cannot read property ‘DEFAULT_PORT’ of undefined at Object.<anonymous> (D:\nodejs\microblog-master\models\db.js:5:74) at Module._compile (module.js:460:26) at Object.Module._extensions...js (module.js:478:10) at Module.load (module.js:355:32) at Function.Module._load (module.js:310:12) at Module.require (module.js:365:17) at require (module.js:384:17) at Object.<anonymous> (D:\nodejs\microblog-master\models\user.js:1:77) at Module._compile (module.js:460:26) at Object.Module._extensions...js (module.js:478:10)

看看别人的源码自己也学习。。

@liuyonggit 应该是和LZ当时用的mongodb版本不一样

应该是settings.js吧?

为什么中文的用户名登录时,在发表微博的时候会报错呢?

@CodeingShow 你好,请问楼主的项目下载下来应该如何连接数据库并运行起来呢(新人捂脸),我按照楼上的改了端口号啥的,但是还是报了好多错,求指导

iiiiiiiiiiiiiiiiiii

回到顶部

AltStyle によって変換されたページ (->オリジナル) /