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

今天我们来搭建博客的首页.

关于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看一下
reset-appa

好, 现在我们就可以在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. 没错的话应该是这个样子滴:
home-page-init

处理逻辑

现在上面有四个按钮显然是不合适的, 合理的做法是根据用户是否登陆来选择显示.
接下来我们来添加处理注册盒登陆的代码.在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, 打开浏览器应该是这样的
before-signup
点击注册按钮
signup
然后
after-signup
点击logout
logout
然后点击login, 用注册过的密码注册
login
登陆之后
after-login

终于, 我们完成了登陆和注册的功能, 由于需要一些初始化的工作, 和这是我第一次写博客, 所以导致这篇有点长…长…长…
如果大家有没看懂的地方, 今天的代码在day02

(今天的逼就装到这里, 谢谢大家.)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值