(五)NodeJs构建web应用

说明:该学习笔记参考《Node.js开发指南》,但是选用的模板和书中不同,添加了自己的理解和适当的补充!仅供参考!

我们打算从零开始用Node.js实现一个微博系统,功能包括路由控制、页面模板、数据库访问、用户注册、登录、用户会话等内容。在这里我们会使用Express框架、MVC设计模式、Jkig模板和MongoDB数据库的操作。

构建项目

1 Express 应用生成器

通过应用生成器工具 express 可以快速创建一个应用的骨架。执行一下命令进行安装到全局环境中

npm install express-generator -g
3248493-3d62a95537360ce1.png
安装应用生成器.png

我们可以看到同时安装了一些依赖。在当前目录下创建blog的应用。

express blog

创建完成后进入应用。应用目录如下:

3248493-8d3c92208166060f.jpg
目录结构

然后安装所有依赖包。

npm install

启动项目。

(windows下)

set DEBUG=blog & npm start

(Linux平台下)

DEBUG=blog npm start

打开浏览器,访问http://127.0.0.1:3000即可看到应用。

选定swig模板引擎

express构建的应用默认使用ejs模板引擎,本人觉得这种奇怪的东东暂时难以接受,之前做过django的项目,最后选定swig,也比较看好它!

1 安装swig

npm install swig

2 在app.js中配置如下:

var swig = require('swig');

var swig  = new swig.Swig();
app.engine('html', swig.readerFile);
app.set('view engine', 'html');

3 模板编写

移除views下的*.jade,创建同名的*.html

编写index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
    Welcome, {{ title }}
</body>
</html>

启动服务

set DEBUG=blog &npm start

打开浏览器访问http://127.0.0.1:3000,能够看到:Welcome, Express

使用Bootstrap和界面设计

我们选定Bootstrap开始设计我们的界面。首先下载,解压之后,将文件夹改名为bootstrap-dist并放在public/中,同时下载最新的jquery,放在public/javascripts/中。

3248493-64988b292b7d5378.png
添加bootstrap文件夹到项目中

这里补充一下Swig的使用参考

修改layout.html中的代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}My Blog{% endblock %}</title>
    {% block head %}
        <link rel="stylesheet" href="/bootstrap-dist/css/bootstrap.min.css">
    {% endblock %}
</head>
<body>
    {% block content %}{% endblock %}
    <script src="/javascripts/jquery-3.2.1.js"></script>
    <script src="/bootstrap-dist/js/bootstrap.js"></script>
</body>
</html>

views/下创建login.html,并编写代码如下:

{% extends 'layout.html' %}

{% block title %}用户登录{% endblock %}

{% block content %}

    <div class="container" style="margin-top: 30px;">
        <div class="row clearfix">
            <div class="col-md-12 column">
                <form class="form-horizontal" role="form" method="post" action="/users/login">
                    <div class="form-group">
                         <label for="inputEmail3" class="col-sm-2 control-label">用户名</label>
                        <div class="col-sm-10">
                            <input type="text" class="form-control" id="inputText3" />
                        </div>
                    </div>
                    <div class="form-group">
                         <label for="inputPassword3" class="col-sm-2 control-label">密码</label>
                        <div class="col-sm-10">
                            <input type="password" class="form-control" id="inputPassword3" />
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-10">
                            <div class="checkbox">
                                 <label><input type="checkbox" />记住我</label>
                            </div>
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-10">
                             <button type="submit" class="btn btn-default">登陆</button>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>

{% endblock %}

目前登录的界面已经创建完成,接下来就是添加路由了。修改routes/users.js文件,添加login GET视图后文件中代码如下:

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource');
});


router.get('/login', function(req, res){
    res.render('login');
})

module.exports = router;

重新启动服务,浏览器访问http://127.0.0.1:3000/users/login,即可以看到登录输入框,则成功!

3248493-ec03d6cd5bef8840.png
登录界面

数据持久化——MongoDB

从官网下载并安装。

连接数据库

打开工
程目录中的package.json,在dependencies属性中添加以下代码代码:

"connect-mongo": ">= 0.1.7",
"mongodb": ">= 0.9.9"

然后运行npm install更新依赖的模块。接下来在工程的目录中创建settings.js文件,这个文件用于保存数据库的连接信息。

将用到的数据库起名为blog

module.exports = {
    cookieSecret: "blogid",
    db: "blog",
    host: "localhost",
    port: 27017
};

db是数据库的名称,host是数据库的地址,port是数据库的端口。cookieSecret用于Cookie加密与数据库无关,我们留作后用。

接下来在项目中创建modules目录,然后在其下中创建db.js

var settings = require("../settings");
var mongodb = require("mongodb");
var Db = mongodb.Db;
var Server = mongodb.Server;

module.exports = new Db(settings.db, new Server(settings.host, settings.port, {}));

以上代码通过module.exports输出了创建的数据库连接。

会话支持

打开app.js添加以下内容:

var session = require('express-session');
var settings = require('./settings');
var MongoStore = require('connect-mongo')(session);

app.use(session({
    secret: settings.cookieSecret,
    store: new MongoStore({
        db: settings.db,
        url: 'mongodb://localhost/blog'  
    })
}));

其中express.cookieParser()是Cookie解析的中间件。express.session()则提供会话支持,设置它的store参数为MongoStore实例,把会话信息存储到数据库中,以避免丢失。

这里需要注意的是在Express4.X版本中,session已经分离出来,所以这里需要去手动下载:

npm install express-session

还需要注意的是store中的url是需要填上去的!这些地方由于书中所用版本较低的原因,这些都是“坑”,就是改这些东西,和查资料折腾了一下午!

注册功能的实现

1 注册界面

设计注册界面在views中添加registered.html

{% extends 'layout.html' %}

{% block title %}用户注册{% endblock %}

{% block content %}

<div class="container" style="margin-top: 30px;width: 500px;">

    {% if success %}
    <div class="alert alert-success alert-dismissable">
        <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
        <h4>
            {{success}}
        </h4>
    </div>

    {% endif %}
    {% if error %}
    <div class="alert alert-danger alert-dismissable">
        <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
        <h4>
            {{error[0]}}
        </h4>
    </div>
    {% endif %}

    <div class="row clearfix">
        <div class="col-md-12 column">
            <form class="form-horizontal" role="form" method="post" action="/users/registered">
                <div class="form-group">
                    <label for="inputText3" class="col-sm-2 control-label">用户名</label>
                    <div class="col-sm-10">
                        <input type="text" class="form-control" id="inputText3" name="username"/>
                    </div>
                </div>
                <div class="form-group">
                    <label for="inputPassword3" class="col-sm-2 control-label">密码</label>
                    <div class="col-sm-10">
                        <input type="password" class="form-control" id="inputPassword3" name="password" />
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                        <div class="checkbox">
                            <label><input type="checkbox" />记住我</label>
                        </div>
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                        <button type="submit" class="btn btn-default">注册</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>

{% endblock %}

界面完成之后,添加路由,在users.js下添加以下内容:

router.get('/registered', function(req, res){
    res.render('registered');
});

浏览器打开http://127.0.0.1:3000/users/registered,这个页面刚开始进去是这样子的:

3248493-18c26bef7da0891c.png
注册界面

当注册失败是这样子的:

3248493-efba69537fd37231.png
注册失败

成功我就不贴了。接下来继续:

2 注册响应

在user.js中添加registeredpost请求:

var User = require('../modules/user')

router.post('/registered', function(req, res){
    if (!(req.body['username'])){
        req.flash("error", '用户名不能为空!');
        return res.redirect("/users/registered");
    }
    var user = new User({
        name: req.body['username'],
        password: req.body['password'],
    });

    // 检查用户是否存在
    User.get(user.name, function(err, has_user){
        if(has_user){
            err = { "errmsg": "用户已存在"};
        }
        if(err){
            req.flash("error", err.errmsg);
            return res.redirect("/users/registered");
        }

        // 保存新用户
        user.save(function(err){
            if(err){
                req.flash("error", err.errmsg);
                return res.redirect("/users/registered");
            }
            req.flash("success", "注册成功!");
            res.redirect("/users/login");
        });
    });
});

在这段代码中:

  • req.body就是POST请求信息解析过后的对象。
  • req.flash是Express提供的一个奇妙的工具,通过它保存的变量只会在用户当前和下一次的请求中被访问,之后会被清除。当然这里使用的时候需要就行一些设置,待会再说。
  • res.redirect是重定向功能。
  • User对象,是接下来要创建的用户模型。实现了用户的判断和保存等。
3 创建用户模型

User是一个描述数据的对象,即MVC架构中的模型。在modules目录下创建user.js,编写内容如下:

var mongodb = require('./db');

function User(user){
    this.name = user.name;
    this.password = user.password;
}

module.exports = User;

User.prototype.save = function save(callback){

    var user = {
        name: this.name,
        password: this.password
    };

    mongodb.open(function(err, db){
        if (err){
            return callback(err);
        }
        db.collection('users', function(err, collection){
            if(err){
                mongodb.close();
                return callback(err);
            }

            // 将name属性添加为索引
            collection.ensureIndex('name', {unique: true});
            // 写入新用户到文档
            collection.insert(user, function(err, user){
                mongodb.close();
                callback(err, user);
            });
        });

    });

};


User.get = function get(username, callback){

    mongodb.open(function(err, db){
        if (err){
            return callback(err);
        }

        db.collection('users', function(err, collection){
            if(err) {
                mongodb.close();
                return callback(err);
            }
            collection.findOne({name: username}, function(err, doc){

                // 对数据库操作完成之后,及时关闭数据库
                mongodb.close();

                // 如果同名的用户存在,那么就直接返回这个用户信息
                if(doc){
                    return callback(err, doc);
                }else{
                    return callback(err, null);
                }
            });

        });
    });
};

以上代码实现了两个接口,User.prototype.saveUser.get,前者是对象实例的方法,用于将用户对象的数据保存到数据库中,后者是对象构造函数的方法,用于从数据库中查找指定的用户。

注意:以上两个接口的类型是不一样的,所以在前面users.js中,是使用User.get(...)var user = new User(..);user.save(...);

4 视图交互

在视图中访问会话中的用户数据,同时为了显示错误和成功的信息,也要增加响应的函数。在app.js中添加以下内容:

var flash = require('connect-flash');
app.use(flash());
app.use(function(req,res,next){
  res.locals.user=req.session.user;

  var err = req.flash('error');
  var success = req.flash('success');
  res.locals.error = err.length ? err : null;
  res.locals.success = success.length ? success : null;
   
  next();
});

'connect-flash也是需要通过npm install connect-flash进行安装的。这里还需要注意的是以上在app.js中添加的代码在文件中的顺序很重要。这点也是一个大坑:

数据库连接在相应函数前面,响应函数在路由前面,这样才能在代码中正确调用flash()方法。

5 小段总结

到这里注册的功能差不多就完成了,假设没有出现意外的话。祝你我都好运,以上代码折腾了一天,但是基本上让自己了解了Express和MongoDB的相互配合使用,也对整个系统的数据流了解的更加透彻。

接下来就是实现登录和首页的问题了,到这里就不贴代码了,会将现在实现的这些代码放在github上,做一个tab,感兴趣的同学可以去看看。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值