您的位置:首页 > Web前端 > Node.js

不完全node实践教程-第二发

2016-04-23 16:27 706 查看
今天我们来搭建博客的首页.

关于express

先讲一下我们需要频繁用到的express的api.

req.query 处理get请求, 获取请求参数. 举个例子;

// GET localhost:/?name=zhu&&school[university]=hust
req.query.name //=> zhu
req.query.school.university //=>hust


req.params 处理get请求 获取类似于/:name形式的参数.举个例子

// GET /user/zhu
app.get('/user/:name', function(req){
req.params.name  // => zhu
//do something
})


req.body 处理post请求, 获取post请求体.举个例子;

// POST user[name]=zhu
req.body.name // => zhu


req.param() 处理get 和 post请求, 依次从params, body, query查找

我们等会儿试一下就知道怎么用这些api了.在这之前我们还要稍微讲一下ejs的东西.

ejs模板

上一发我们已经将模板index.ejs改成了hello world的形式. 我提到过render的过程实际上就是将传过来的参数最相应的’字符串’进行替换. 这些字符串是由三种标签组成的.

<% code %> 这种形式的用来写js代码, 控制逻辑

<%= string %> 这种形式用来显示要替换的字符串, 上一发的title就是这种

<%- include -% 这种形式的是用啦包含其他的模板, 方便模板的复用.

举一些例子:

// 从express传过来的数据是 users: ['zhu', 'hai', 'hao']
<ul>
<% for (var i = 0; i < users.length; i++){%>
<li><%= users[i]%></li>
<% } %>
</ul>
// 替换之后的结果是
<ul>
<li>zhu</li>
<li>hai</li>
<li>hao</li>
</ul>


我们有一个模板a.ejs

i am a


还有一个b.ejs

<%- include a %>
i am b


那么渲染之后, b.ejs及时这个样子滴

i am a
i am b


好, 我们终于可以开始干活了!

开始干活

设计总体架构

我这个水平的同学..看到设计架构就会有一种刁刁哒的感觉.(我也装一逼).说人话就是设计一下这个博客要有哪些功能

可以注册

可以登陆

可以登出

好像少了点什么…哦对还要可以

登陆的用户可以发表文章

首页显示最近发的博客(当然这有点无脑, 暂时先这样计划)

设计路由

我们先来设计一下路由

/: 首页

/signup: 注册

/login: 登陆

/logout: 登出

/post: 发表文章

我们来看一下现在的app.js

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

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

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

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;


1-6行 分别是引入一些必要的模块.

8-9行 是引入两个路由的模块

11行是实例化一个express对象

14-15行 是设置模板的静态文件防止的目录, 然后设置模板为ejs,也就是说我们可以设置成其他的.

19-23行 分别是加载日志中间件, json解析中间件, 解析urlencoded的中间件, 解析cookie的中间件, 设置静态文件目录

25-26行 设置请求盒路由处理的对应规则

29-33 51-57行分别用一个函数来处理httpcode400和500的问题

60行 导出模块让/bin/www使用

讲道理我们应该把要设置的路由规则添加到app.js里面. 但是想到后面我们可能要添加更多的路由, 这样会显得app.js有点杂乱.所以我们把app.js修改成这样

var routes = require('./routes/index');
var app = express();
// some thing
routes(app);


然后把index.js改成这样

module.exports = function(app) {
app.get('/', function(req, res, next) {
res.render('index', { title: 'world' });
});
}


我们再运行app看一下



好, 现在我们就可以在index里面添加路由了.在/routes新建文件login.js, signup.js, logout.js, post.js然后修改index.js

var express = require('express');
var router = express.Router();
var login = require('./login');
var signup = require('./signup');
var logout = require('./logout');
var post = require('./post')

module.exports = function(app) {
app.get('/', function(req, res, next) {
res.render('index', { title: 'world' });
});

app.use('/signup', signup);
app.use('login', login);
app.use('logout', logout);
app.use('post', post);
}


让app和数据库一起工作

在进行下一步操作之前我们要下两个包.一个是支持mongodb的, 一个是支持回话session的.

打开终端, 在项目根目录敲下如下命令

$ sudo npm install mongodb express-session connect-mongo --save


安装完成之后.

在根目录创建setting.js 用来保存数据库的配置. 新建model文件夹, 用来盛放模型文件. 并且在model文件夹新建文件database.js. 然后分别写入以下内容.

database.js

var mongoDb = require('mongodb').MongoClient;

var dbState = {
db: null
};

exports.connect = function(url, callback) {
if (dbState.db) return callback();
mongoDb.connect(url, function(err, db) {
if(err) return callback(err);
dbState.db = db;
return callback();
});
};

exports.get = function() {
return dbState.db;
};

exports.close = function(callback) {
if(dbState.db){
dbState.db.close(function(err, result) {
if(err) callback(err);
dbState.db = null;
});

}
};


setting.js

module.exports = {
cookieSecret: 'share',
db: 'share',
host: 'host',
post: 27017
};


然后在app.js添加:

db.connect(dbUrl, function(err) {
if (err) {
console.log("can not connect to mongo");
process.exit(0);
}
console.log("connect to mongo success");
});


这样一来, 只要我们在运行app的时候, 就会自动链接数据库, 而且用dbStates保存数据库链接的状态, 连接一次, 复用多次, 可以说是单例模式的运用.

然后, 然后..我们终于可以开始设计首页, 注册和登陆的前端页面了. 在view里面新建signup.ejs, login.ejs, 如果要加样式的话, 把相应的样式文件写在/public/stylesheets里面.然后注意引入的时候路径是/stylesheets/xxx.css, 因为在app.js已经设置了静态文件的根目录是/public.

代码我这里就不直接给出了.大家可以在这里获取. 如果你觉得我的太丑也可以自己diy…

一切都弄好之后, 再运行app. 没错的话应该是这个样子滴:



处理逻辑

现在上面有四个按钮显然是不合适的, 合理的做法是根据用户是否登陆来选择显示.

接下来我们来添加处理注册盒登陆的代码.在model文件夹新建文件user.js, 添加以下代码:

var mongodb = require('./database');
var assert = require('assert');
function User(user) {
this.na = user.na;
this.password = user.password;
this.email= user.email;
}
module.exports = User;

User.prototype.save = function(callback) {
var user = {
name: this.na,
password: this.password,
email: this.email
};

mongodb.get().collection('users', function(err, collection) {
if (err) {
mongodb.close();
return callback(err);
}
assert('string', !typeof(user.name), user.name );
collection.insert(user, function(err, user) {
if (err) {
return callback(err);
}
assert.equal('object', typeof(user), "can not insert user");
callback(null, user);
});
});
};

User.getByName = function(name, callback) {
var doc = {
name: name
};

mongodb.get().collection('users', function(err, collection) {
if(err) {
mongodb.close();
return callback(err);
}
collection.findOne(doc, function(err, user) {
if (err) {
return callback(err);
}
callback(null, user);
});
});
};


user分别有两个方法, 一个是保存用户信息的实例. 一个是通过名字获取那个户信息.然后在routes文件夹里面的signup.js 和login.js分别添加以下代码.

var crypto = require('crypto');
var User = require('../model/user');
var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
res.render('signup', {title: 'signup'});
});

router.post('/' ,function(req, res, next) {
var name = req.body.name,
password = req.body.pwd,
password_re = req.body.pwd_re;
email = req.body.email;

if (password != password_re) {
req.flash('error', "two passsword is different");
return res.redirect('/signup');
}
var md5 = crypto.createHash('md5'), password_hex = md5.update(password).digest('hex');
var newUser = new User ({ na: name, password: password_hex, email: email });
console.dir(newUser);
User.getByName(name, function(err, user) {
if (err) { req.flash('error', err);
return res.redirect('/');
}
if (user) {

req.flash('error', "user has already exist");
return res.redirect('/login');
}
newUser.save(function(err, user) {
if (err) {
return res.redirect('/');
}
req.session.user = user;

return res.redirect('/');
});

});
});

module.exports = router;


第21和22行是用对用户密码进行md5加密. 然后存储进数据库.

var crypto = require('crypto');
var User = require('../model/user');
var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
res.render('login', {title: 'login', user: req.session.user});
});

router.post('/', function(req, res, next) {
var name = req.body.name;
var password = req.body.pwd;

var md5 = crypto.createHash('md5'),
password_hex = md5.update(password).digest('hex');

var newUser = new User ({
na: name,
password: password_hex,
email: ""
});

User.getByName(name, function(err, user) {
if (!user) {
res.redirect('/login');
}
if (password_hex != user.password) {
req.flash("error", "password wrong!");
res.redirect("./login");
}
req.session.user = user;
res.redirect("/");
});
});

module.exports = router;


最后, 打开header.ejs模板(首页的代码可以从我的github找到.首页由header.ejs和content.ejs,footer.ejs组合而成), 修改内容为

<nav class='header-right'>
<ul>
<li>
<% if(user) { %>
<a class='button post' href='/post'>post</a >
<% } else { %>
<a class='button login' href='/login'>login</a >
<% } %>
</li>
<li>
<% if(user) { %>
<a class='button logout ' href='/logout'>logout</a >
<% } else { %>
<a class='button reg ' href='/signup'>signup</a >
<% } %>
</li>
<% if (user) {%>
<li class="user"><a class='link' href='/u/<%= user.name%>'><%= user.name %></a ></li>
<% } else {%>
<li class="home"><a class='link' href='/'>home</a ></li>
<% }%>
</ul>
</nav>


这样一来, 首页显示的按钮及时根据用户是否登陆而不同.

如果以上步骤都没有错, (这几乎是不可能的) 如果出错了, 请用以下步骤解决.

根据提示的错误查找代码看是否有明显的逻辑错误或者语法错误.大部分的错误都是这样引起的.

如果解决不了, 在博客下面留言, 或者在github issue区贴出来.

根据别的同学的回复查找错误.如果继续出错,回到第一步.

假设成功运行app, 打开浏览器应该是这样的



点击注册按钮



然后



点击logout



然后点击login, 用注册过的密码注册



登陆之后



终于, 我们完成了登陆和注册的功能, 由于需要一些初始化的工作, 和这是我第一次写博客, 所以导致这篇有点长…长…长…

如果大家有没看懂的地方, 今天的代码在day02

(今天的逼就装到这里, 谢谢大家.)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: