本实例已经将完整代码上传github了,网址 https://github.com/tonyzhan/microblog欢迎大家下载!
《nodejs开发指南》是一本好书,但微博开发实例已经过时了。express4.x发布了,Jade模板是express的默认模板。学习了jade模板后发觉也没什么难的,而且html代码清爽了许多。网上好多人说ejs模板怎么好,我却觉得仿佛回到了asp时代,凡是像microsoft的东西,我都不感冒。我使用express4.x版和Jade模板重写《nodejs开发指南》微博实例,请大家参考,欢迎大家多提意见。
Installation 安装
$ npm install -g 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的代码:
## index.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;
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}
转自:
https://cnodejs.org/topic/53f23e198f44dfa35129c43b