利用mongoose第三方库进行业务数据操作, 1、开始

作者: 前端  发布:2019-11-09

NodeJS 开发者的 10 个常见错误

2015/06/16 · CSS · 1 评论

本文由 伯乐在线 - 刘健超-J.c 翻译,黄利民 校稿。未经许可,禁止转载!
英文出处:www.toptal.com。欢迎加入翻译组。

自 Node.js 公诸于世的那一刻,就伴随着赞扬和批评的声音。这个争论仍在持续,而且并不会很快消失。而我们常常忽略掉这些争论产生的原因,每种编程语言和平台都是因某些问题而受到批评,而这些问题的产生,是取决于我们如何使用这个平台。不管有多难才能写出安全的 Node.js 代码,或有多容易写出高并发的代码,该平台已经有相当长一段时间,并已被用来建立一个数量庞大、稳健和成熟的 web 服务器。这些 web 服务器伸缩性强,并且它们通过在 Internet 上稳定的运行时间,证明自己的稳定性。

然而,像其它平台一样,Node.js 容易因开发者问题而受到批评。一些错误会降低性能,而其它一些问题会让 Node.js 直接崩溃。在这篇文章里,我们将会聊一聊关于 Node.js 新手的 10 个常犯错误,并让他们知道如何避免这些错误,从而成为一名 Node.js 高手。

图片 1

本章主要讲什么(一句话)?

本章主要讲解:利用mongoose第三方库进行业务数据操作

图片 2

                                                  --  

#  Node.js学习笔记

 1、开始运用mongoose时,得先安装,打开命令行,执行$ npm install mongoose

错误 #1:阻塞事件循环

JavaScript 在 Node.js (就像在浏览器一样) 提供单线程执行环境。这意味着你的程序不能同时执行两部分代码,但能通过 I/O 绑定异步回调函数实现并发。例如:一个来自Node.js 的请求是到数据库引擎获取一些文档,在这同时允许 Node.js 专注于应用程序其它部分:

JavaScript

// Trying to fetch an user object from the database. Node.js is free to run other parts of the code from the moment this function is invoked.. // 尝试从数据库中获取一个用户对象。在这个函数执行的一刻,Node.js 有空去运行代码其它部分.. db.User.get(userId, function(err, user) { // .. until the moment the user object has been retrieved here // .. 直到用户对象检索到这里的那一刻 })

1
2
3
4
5
6
// Trying to fetch an user object from the database. Node.js is free to run other parts of the code from the moment this function is invoked..
// 尝试从数据库中获取一个用户对象。在这个函数执行的一刻,Node.js 有空去运行代码其它部分..
db.User.get(userId, function(err, user) {
// .. until the moment the user object has been retrieved here
        // .. 直到用户对象检索到这里的那一刻
})

图片 3

然而,具有计算密集型代码的 Node.js 实例被数以万计客户端同时连接执行时,会导致阻塞事件循环,并使所有客户端处于等待响应状态。计算密集型代码,包括尝试给一个庞大数组进行排序操作和运行一个格外长的循环等。例如:

JavaScript

function sortUsersByAge(users) { users.sort(function(a, b) { return a.age > b.age ? -1 : 1 }) }

1
2
3
4
5
function sortUsersByAge(users) {
users.sort(function(a, b) {
return a.age > b.age ? -1 : 1
})
}

基于小 “users” 数组执行 “sortUserByAge” 函数,可能没什么问题,当基于庞大数组时,会严重影响整体性能。如果在不得不这样操作的情况下,你必须确保程序除了等待事件循环而别无他事(例如,用 Node.js 建立命令行工具的一部分,整个东西同步运行是没问题的),然后这可能没问题。然而,在 Node.js 服务器实例尝试同时服务成千上万个用户的情况下,这将是一个毁灭性的问题。

如果用户数组是从数据库检索出来的,有个解决办法是,先在数据库中排序,然后再直接检索。如果因需要计算庞大的金融交易历史数据总和,而造成阻塞事件循环,这可以创建额外的worker / queue 来避免阻塞事件循环。

正如你所看到的,这没有新技术来解决这类 Node.js 问题,而每种情况都需要单独处理。而基本解决思路是:不要让 Node.js 实例的主线程执行 CPU 密集型工作 – 客户端同时链接时。

一、前言

上一章主要对项目引入MongoDB进行数据存储,并导入mongoose第三方组件,完成mongodb数据库配置及连结代码,本节继续。

## 简介

- 编写高性能网络服务器的JavaScript工具包

- 单线程、异步、事件驱动

- 特点:快,耗内存多

- PHP是单线程,耗内存少速度相对慢些

2、连接mongodb数据库,在app.js里面添加如下两行代码。

错误 #2:调用回调函数多于一次

JavaScript 一直都是依赖于回调函数。在浏览器中,处理事件是通过调用函数(通常是匿名的),这个动作如同回调函数。Node.js 在引进 promises 之前,回调函数是异步元素用来互相连接对方的唯一方式 。现在回调函数仍被使用,并且包开发者仍然围绕着回调函数设计 APIs。一个关于使用回调函数的常见 Node.js 问题是:不止一次调用。通常情况下,一个包提供一个函数去异步处理一些东西,设计出来是期待有一个函数作为最后一个参数,当异步任务完成时就会被调用:

JavaScript

module.exports.verifyPassword = function(user, password, done) { if(typeof password !== ‘string’) { done(new Error(‘password should be a string’)) return } computeHash(password, user.passwordHashOpts, function(err, hash) { if(err) { done(err) return } done(null, hash === user.passwordHash) }) }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports.verifyPassword = function(user, password, done) {
if(typeof password !== ‘string’) {
done(new Error(‘password should be a string’))
return
}
 
computeHash(password, user.passwordHashOpts, function(err, hash) {
if(err) {
done(err)
return
}
 
done(null, hash === user.passwordHash)
})
}

注意每次调用 “done” 都有一个返回语句(return),而最后一个 “done” 则可省略返回语句。这是因为调用回调函数后,并不会自动结束当前执行函数。如果第一个 “return” 注释掉,然后给这个函数传进一个非字符串密码,导致 “computeHash” 仍然会被调用。这取决于 “computeHash” 如何处理这样一种情况,“done” 可能会调用多次。任何一个人在别处使用这个函数可能会变得措手不及,因为它们传进的该回调函数被多次调用。

只要小心就可以避免这个 Node.js 错误。而一些 Node.js 开发者养成一个习惯是:在每个回调函数调用前添加一个 return 关键字。

JavaScript

if(err) { return done(err) }

1
2
3
if(err) {
return done(err)
}

对于许多异步函数,它的返回值几乎是无意义的,所以该方法能让你很好地避免这个问题。

二、技术关健词

Node、MongoDB、Angular2、mongoose

## 在Linux上安装node

  1. 先安装一个[nvm](:

- 进入nvm的GitHub点击README的installation

- 找到下列代码(为了获取最新的版本)复制下  来输入到Ubuntu的命令行中

```bash

curl -o- | bash

```

- 安装完要重启一下

- 激活nvm

```bash

echo ". ~/.nvm/nvm.sh" >> /etc/profile

source /etc/profile

```

- 输入nvm测试,如果报错:

```bash

Computing checksum with shasum -a 256 Checksums do not match:

```

- 就再输入:

```bash

export NVM_DIR="$HOME/.nvm"

#不行就再输入

. "/usr/local/opt/nvm/nvm.sh"

```

- 如果还报错去[官网](

  1. nvm安装完成后输入:

```bash

nvm install --lts

nvm use (你安装的版本号)

```

- 安装最新稳定版,并使用它

- 输入node,如果进入了node交互环境就安装成功了

- 如果要查看已经安装的node版本,输入:

```bash

nvm ls

```

  1. 完善安装

- 上述过程完成后,有时会出现,当开启一个新的 shell 窗口时,找不到 node 命令的情况,这种情况一般来自两个原因:

- 一、shell 不知道 nvm 的存在

- 二、nvm 已经存在,但是没有 default 的 Node.js 版本可用。

- 解决方式:

- 一、检查 ~/.profile 或者 ~/.bash_profile 中有没有这样两句

```bash

export NVM_DIR="/Users/YOURUSERNAME/.nvm"

[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"  # This loads nvm

```

- 没有的话,加进去。这两句会在 bash 启动的时候被调用,然后注册 nvm 命令。

- 二、调用

```bash

nvm ls

```

- 看看有没有 default 的指向。如果没有的话,执行

```bash

$ nvm alias default (你安装的版本号)

#再

$ nvm ls

#看一下

```

## 基本HTTP服务器

```javascript

//server.js

var http = require('http');                    //导入Node.js自带的HTTP模块

http.createServer(function(request,response){  //调用HTTP模块的createServer()函数创建一个服务,该函数有两个参数:request和response它们是对象,用它们的方法来处理HTTP请求的细节,并且响应请求

    response.statusCode = 200;                  //返回的状态码

    response.setHeader('Content-Type', 'text/plain');  //HTTP协议头输出类型

    response.end();                            //结束HTTP请求,不写就没有HTTP协议尾

}).listen(8000);                                //监听8000端口

console.log("Server running at ")

```

- createServer()方法接受一个方法作为参数

- 如果只是上面的一个简单服务,浏览器访问时会提交两次HTTP请求(express框架中已经清除)

- 如果不用express框架,需手工清除:

```javascript

if(request.url !== "/favicon.ico"){    //清除第2次访问:因为大部分浏览器都会在你访问 时尝试读取

    response.write();

    response.end();

}

```

## 调用其它函数

### 调用本文件内的函数

```javascript

//server.js

var http = require('http');

http.createServer(function(request,response){

    response.statusCode = 200;

    response.setHeader('Content-Type', 'text/plain');

    test(response);    //直接调用

    response.end();

}).listen(8000);

console.log("Server running at ");

function test(res){

    res.write("hello world!")

}

```

### 调用其它文件内的函数

- 原文件

```javascript

//server.js

var http = require('http');

var test = require('./test.js')

http.createServer(function(request,response){

    response.statusCode = 200;

    response.setHeader('Content-Type', 'text/plain');

    //调用方式1:

    test.hello(response);

    test.world(response);

    //方式2:

    test['hello'](response);

    test['world'](response);

    //方法2更为常用,因为可以通过:

    var funname = 'hello';

    test[funname](response)

    //这种方式,改变字符串(funname)从而调用想要的函数

    response.end();

}).listen(8000);

console.log("Server running at ");

```

- 要调用的文件

```javascript

//test.js

//形式1:

function hello(res){

    res.write("hello");

};

function world(res){

    res.write("world");

};

module.exports = hello;    //只支持一个函数

//形式2:支持多个函数

module.exports = {

    hello: function(res){

        res.write("hello");

    },

    world: function(res){

        res.write("world");

    }

}

```

- 只有用module.exports导出,此文件内的方法才能被其他文件调用

## 模块的调用

- JavaScript中类的写法:

```javascript

//user.js

function user (id,name,age){

    this.id=id;

    this.name=name;

    this.age=age;

    this.self=function(){

        console.log(this.name+"is"+this.age+"years old");

    }

}

module.exports = user;

```

- 调用

```javascript

//server.js

var http = require('http');

var user = require('./user')

http.createServer(function(request,response){

    response.statusCode = 200;

    response.setHeader('Content-Type', 'text/plain');

    user1 = new user(1,"Rain",20);

    user1.self();

    response.end();

}).listen(8000);

console.log("Server running at ");

```

- ps: JavaScript中类的继承:

```javascript

var user = require('./user');

function admin (id,name,age){

    user.apply(this,[id,name,age]);

    this.idis=function(res){

        res.write(this.name+"is the"+this.id+"th");

    }

}

module.exports = admin;

```

## 路由

- 通过解析url获取路由的字符串,调用路由文件内对应的方法,通过该方法读取对应的HTML文件,再将HTML文件返回给客户端

```javascript

//server.js

var http = require('http');

var url = require('url');          //node.js提供一个“url”对象来解析url

var router = require('./router');  //引入路由文件

http.createServer(function(request,response){

    response.statusCode = 200;

    response.setHeader('Content-Type', 'text/plain');

    var pathname = url.parse(request.url).pathname;    //通过url.pathname方法解析出url后面的路由

    pathname = pathname.replace(///,'')                //通过正则将路由字符串的斜杠头给去掉

    router[pathname](request,response);                //通过pathname字符串调用路由中对应的方法

    response.end();

}).listen(8000);

console.log("Server running at ");

```

- 路由文件:router.js

```javascript

//router.js

module.exports={

    login: function(req,res){

        res.write("转到login页面")

    }

    register: function(req,res){

        res.write("转到注册页面")

    }

}

```

## 读取文件

### 同步读取文件(不推荐)

- 读取文件的配置文件:optfile.js

```javascript

//optfile.js

var fs = require('fs');                //node.js提供的操作文件模块

module.exports={

    readfileSync: function (path) {    //同步读取方法

        var data = fs.readFilesSync(path,'utf-8');  //通过fs的readFilesSync方法同步读取文件,path为文件路径,以utf-8的编码读取

        return data;

    },

    readfile: function (path) {        //异步读取方法

        fs.readFile(path,function(err,data){

            if (err){

                console.log(err);

            } else {

                return data

            };

            console.log("函数执行完毕");

        });

    }

}

```

```javascript

//server.js

var http = require('http');

var optfile = require('./optfile.js');  //引入配置文件

http.createServer(function(request,response){

    response.statusCode = 200;

    response.setHeader('Content-Type', 'text/plain');

    optfile.readfileSunc('.src/login.html')  //路径是相对于此文件的

    response.end("主程序执行完毕");

}).listen(8000);

console.log("Server running at ");

```

- node.js的高性能主要就是依靠异步操作,在异步时,当服务器执行读文件的操作时程序会继续执行下去,而不是在原地等文件读取完毕,因此上述代码会在控制台依次输出:

```bash

函数执行完毕

主程序执行完毕        #文件还未读取完主程序就已经结束了(response.end)

(最后返回读取到的文件内容“data”)

```

- 而正是由于这种异步操作,如果想要将读取的文件内容返回给客户端,就要使用“闭包”的方式

```javascript

//server.js

var http = require('http');

var optfile = require('./optfile.js');  //引入配置文件

http.createServer(function(request,response){

    response.statusCode = 200;

    response.setHeader('Content-Type', 'text/plain');

    function recall(data){              //创建闭包函数

        response.write(data);          //它可以储存response

        response.end();

    }

    optfile.readfileSunc('.src/login.html',recall)  //路径是相对于此文件的

}).listen(8000);

console.log("Server running at ");

```

```javascript

//optfile.js

var fs = require('fs');

module.exports={

    readfileSync: function (path) {    //同步读取方法

        var data = fs.readFilesSync(path,'utf-8');

        return data;

    },

    readfile: function (path,recall) {        //异步读取方法,接入闭包函数

        fs.readFile(path,function(err,data){

            if (err){

                console.log(err);

            } else {

                recall(data);                  //因为是闭包函数recall会储存response

            };

            console.log("函数执行完毕");

        });

    }

}

```

## 写文件

- 在optfile.js中加入写文件功能:

```javascript

//optfile.js

var fs = require('fs');

module.exports = {

    writefile: function (path,recall) {

        fs.readFile(path, function(err,data){      //异步方式

            if(err){

                console.log(err)

            } else {

                console.log("It's saved!");        //文件被保存

                recall("写入文件成功");

            }

        })

    }

}

```

- 在server.js中加入:

```javascript

optfile.writefile("./src/data.json","这里传入想要写入的数据",recall);

```

- PS:实际开发过程中,这些函数一般是不放在server.js中的,应该放在路由配置文件中,配置相应的写数据接口

## 读取并显示图片

- 在optfile.js中加入写文件功能:

```javascript

readImg: function(path,res){

    fs.readFile(path,"binary",function(err,filedata){  //binary参数代表读取的是一个二进制流的文件

        if(err){

            console.log(err);

            return;

        } else {

            res.write(filedata,"binary");              //用“binary”的格式发送数据

            res.end();

        }

    })

}

```

- 这种方法只能单独读取图片

## 参数接受

- 只需修改路由文件中的:

```javascript

var querystring = require('querystring');      //需要引入querystring模块来解析POST请求体中的参数

confirm: function (req,res) {

    //get方式接收参数,处理是同步的

    var rdata = url.parse(req.url,true).query;

    if(rdata['email'] != undefined){

        console.log("email:"+rdata['email']+","+"password:"+rdata['password']);

    };

    //post方式接收参数,处理是异步的

    var post = '';

    req.on('data',function(chunk){

        post += chunk;

    });

    req.on('end',function(){

        post = querystring.parse(post);

        console.log(post['email']+","+post['password']);

    });

}

```

## 动态数据渲染

- 在使用路由传输文件的时候如果想要将页面中某些字段动态的替换成对应的数据,可以在数据(data)传输时先将其字符化(toString()),再使用正则将匹配到的标识字符进行替换,如vue中就使用"{{}}"双大括号标识数据位置,再进行匹配替换为对应数据

- 如修改回调函数

```javascript

function recall(data){

    dataStr = data.toString();

    for(let i=0;i

        re = new RegExp("{"+arr[i]+"}",g);  //用正则匹配标识数据(这里我用“{}”一个大括号标识数据)

        dataStr = dataStr.replace(re,post[arr[i]])  //从数据库中循环替换对应的数据

    }

    res.write(dataStr);

    res.end();

}

```

## 异步流程控制

- 当有某些操作需要依赖于上一步操作完成后才能执行,因为node里的操作大都是异步的,这就需要对异步流程进行控制

- node.js提供了一个异步流程控制对象async

  1. 串行无关联:async.series  (依次执行,此步执行的结果不影响下一个程序)

  2. 并行无关联:async.parallel (同时执行,正常的异步)

  3. 串行有关联:waterfall  (瀑布流)

- async需要另外安装:

```bash

npm install async --save-dev

```

## 连接MySQL数据库

### 直接连接(不常用,需了解)

- 安装MySQL支持:

```bash

npm install mysql

```

- 创建一个新的数据库,建表:

```mysql

create table user(

    uid int not null primary key auto_increment,    //id自增长

    uname varchar(100) not null,

    pwd varchar(100) not null

)ENGINE=InnoDB DEFAULT CHARSET=utf8;

```

- 直接连接MySQL并进行操作,下面列举了一些常用的操作,在实际操作过程中一般把这些操作封装到对应的方法中,需要的时候直接调用

```javascript

var mysql = require("mysql")    //调用MySQL模块

//创建一个connection

var connection = mysql.createConnection({

    host: 'localhost',  //主机

    user: 'root',      //MySQL认证用户名

    password: "",      //MySQL认证用户密码

    database: "rain",  //数据库名

    port: '3306'        //端口号

});

//打开connection连接

connection.connect(function(err){

    if(err){

        console.log('[query] - :'+err);

        return;

    }

    console.log("[connection connect] succeed");

})

//向表中插入数据

var userAddSql = "insert into user (uname,pwd) values(?,?)";    //要执行的MySQL语句,value的问号是占位符

var param = ['test','test'];                                    //要插入的数据可以通过变量传值,这里用字符串测试

connection.query(userAddSql,param,function(err,rs){            //调用query方法插入数据,第一个参数是执行的MySQL语句,第二个是插入的数据,第三个是匿名回调函数处理报错;传的值一定要和MySQL语句匹配

    if(err){

        console.log("insert err:",err.message);

        return;

    }

    console.log(rs);    //rs是插入成功后返回的一些参数

});

//执行查询

//群体查询

connection.query('SELECT * from user',function(err,rs,fields){

    if(err){

        console.log('[query] - :'+err);

        return;

    }

    for(let i=0;i

        console.log('The solution is: ',rs[i].uname);

    }

    console.log(fields);        //fields为查询产生的信息,不是查询结果,一般没什么用

})

//单独查询

connection.query('SELECT * from user where uid=?',[2],function(err,rs,fields){  //查询uid=2的那条数据

    if(err){

        console.log('[query] - :'+err);

        return;

    }

    for(let i=0;i

        console.log('The solution is: ',rs[i].uname);

    }

    console.log(fields);

})

//关闭connection连接

connection.end(function(err){

    if(err){

        console.log(err.toString());

        return;

    }

    console.log("[connection end] succeed");

})

```

- 这里列举的操作都是较为常用的,还有其它的操作可以直接通过修改传入的MySQL语句来完成

### 连接池连MySQL(常用,效率高)

- 原理:

- 创建MySQL连接的开销十分巨大

- 使用连接池 server启动时会创建10-20个连接放在连接池中,当有访问需连接MySQL数据库时,就从连接池中取出一个连接,进行数据库操作,操作完成后,再将连接放回连接池

- 连接池会自动的管理连接,当连接较少时,会减少连接池中的连接,当连接量较大时,会扩充连接

- 使用node提供的连接池需安装node-mysql模块:

```bash

npm install node-mysql -g

```

#### 操作连接池

```javascript

//optPool.js

var mysql = require('mysql');

function optPool(){        //创建一个连接池的类方便使用

    this.flag = true;      //用来标记是否连接过

    this.pool = mysql.createPool({

        host: 'localhost',  //主机

        user: 'root',      //MySQL认证用户名

        password: "",      //MySQL认证用户密码

        database: "rain",  //数据库名

        port: '3306'        //端口号

    });

    this.getPool = function(){  //初始化pool

        if(this.flag){

            //监听connection事件

            this.pool.on('connection',function(connection){

                connection.query('SET SESSION auto_increment_increment=1');

                this.flag = false;

            });

        }

        return this.pool;

    }

};

module.exports = optPool;  //导出为一个类

```

```javascript

var optPool = require('./optPool');

var optpool = new optPool();

var pool = optpool.getPool();

//从连接池中获取一个连接

pool.getConnection(function(err,connect){  //如果操作成功就拿到连接(connect)

    //做一个插入操作

    var userAddSql = "insert into user (uname,pwd) values(?,?)";

    var param = ['test','test'];

    connect.query(userAddSql,param,function(err,rs){    //异步操作

        if(err){

            console.log("insert err:",err.message);

            return;

        }

        console.log('success');

        connect.release()  //将连接放回连接池

    });

})

```

[javascript]
var mongoose = require('mongoose'); 
mongoose.connect('mongodb://localhost/test'); //连接到一个test的数据库 

错误 #3:函数嵌套过深

函数嵌套过深,时常被称为“回调函数地狱”,但这并不是 Node.js 自身问题。然而,这会导致一个问题:代码很快失去控制。

JavaScript

function handleLogin(..., done) { db.User.get(..., function(..., user) { if(!user) { return done(null, ‘failed to log in’) } utils.verifyPassword(..., function(..., okay) { if(okay) { return done(null, ‘failed to log in’) } session.login(..., function() { done(null, ‘logged in’) }) }) }) }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function handleLogin(..., done) {
db.User.get(..., function(..., user) {
if(!user) {
return done(null, ‘failed to log in’)
}
utils.verifyPassword(..., function(..., okay) {
if(okay) {
return done(null, ‘failed to log in’)
}
session.login(..., function() {
done(null, ‘logged in’)
})
})
})
}

图片 4

任务有多复杂,代码就有多糟糕。以这种方式嵌套回调函数,我们很容易就会碰到问题而崩溃,并且难以阅读和维护代码。一种替代方式是以函数声明这些任务,然后将它们连接起来。尽管,有一种最干净的方法之一 (有争议的)是使用 Node.js 工具包,它专门处理异步 JavaScript 模式,例如 Async.js :

JavaScript

function handleLogin(done) { async.waterfall([ function(done) { db.User.get(..., done) }, function(user, done) { if(!user) { return done(null, ‘failed to log in’) } utils.verifyPassword(..., function(..., okay) { done(null, user, okay) }) }, function(user, okay, done) { if(okay) { return done(null, ‘failed to log in’) } session.login(..., function() { done(null, ‘logged in’) }) } ], function() { // ... }) }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function handleLogin(done) {
async.waterfall([
function(done) {
db.User.get(..., done)
},
function(user, done) {
if(!user) {
return done(null, ‘failed to log in’)
}
utils.verifyPassword(..., function(..., okay) {
done(null, user, okay)
})
},
function(user, okay, done) {
if(okay) {
return done(null, ‘failed to log in’)
}
session.login(..., function() {
done(null, ‘logged in’)
})
}
], function() {
// ...
})
}

类似于 “async.waterfall”,Async.js 提供了很多其它函数来解决不同的异步模式。为了简洁,我们在这里使用一个较为简单的案例,但实际情况往往更糟。

三、本章涉及核心技术点

图片 5

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test'); //连接到一个test的数据库操作到这里基本上是个人都会,不过接下来具体如何进行数据库的操作,在没有实例的情况下还是有点纠结的。我这里以一个登录注册为例来说明如何使用。

错误 #4:期望回调函数以同步方式运行

异步程序的回调函数并不是 JavaScript 和 Node.js 独有的,但它们是造成回调函数流行的原因。而对于其它编程语言,我们潜意识地认为执行顺序是一步接一步的,如两个语句将会执行完第一句再执行第二句,除非这两个语句间有一个明确的跳转语句。尽管那样,它们经常局限于条件语句、循环语句和函数调用。

然而,在 JavaScript 中,回调某个特定函数可能并不会立刻运行,而是等到任务完成后才运行。下面例子就是直到没有任何任务,当前函数才运行:

JavaScript

function testTimeout() { console.log(“Begin”) setTimeout(function() { console.log(“Done!”) }, duration * 1000) console.log(“Waiting..”) }

1
2
3
4
5
6
7
function testTimeout() {
console.log(“Begin”)
setTimeout(function() {
console.log(“Done!”)
}, duration * 1000)
console.log(“Waiting..”)
}

你会注意到,调用 “testTimeout” 函数会首先打印 “Begin”,然后打印 “Waiting..”,紧接大约一秒后才打印 “Done!”。

任何一个需要在回调函数被触发后执行的东西,都要把它放在回调函数内。

四、内容

3、用webstorm创建一个新的Express App项目,这样就直接封装好了express(),省去了自己写的麻烦。然后修改app.js如下:

错误 #5:用“exports”,而不是“module.exports”

Node.js 将每个文件视为一个孤立的小模块。如果你的包(package)含有两个文件,或许是 “a.js” 和 “b.js”。因为 “b.js” 要获取 “a.js” 的功能,所以 “a.js” 必须通过为 exports 对象添加属性来导出它。

JavaScript

// a.js exports.verifyPassword = function(user, password, done) { ... }

1
2
// a.js
exports.verifyPassword = function(user, password, done) { ... }

当这样操作后,任何引入 “a.js” 模块的文件将会得到一个带有属性方法 “verifyPassword” 的对象:

JavaScript

// b.js require(‘a.js’) // { verifyPassword: function(user, password, done) { ... } }

1
2
// b.js
require(‘a.js’) // { verifyPassword: function(user, password, done) { ... } }

然而,如果我们想直接导出这个函数,而不是作为某个对象的属性呢?我们能通过覆盖 exports 对象来达到这个目的,但我们不能将它视为一个全局变量:

JavaScript

// a.js module.exports = function(user, password, done) { ... }

1
2
// a.js
module.exports = function(user, password, done) { ... }

注意,我们是如何将 “exports” 作为 module 对象的一个属性。在这里知道 “module.exports” 和 “exports” 之间区别是非常重要的,并且这经常会导致 Node.js 开发新手们产生挫败感。

4.1、新建users.js组件来封装【用户】模块功能

废话不多说,先代码奉上:

书接上文,直接在models文件下新建一users.js文件,代码如下:


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

function User(user) {

this.name = user.name;

this.password = user.password;

this.email = user.email;

};

module.exports = User;

//存储用户信息

User.prototype.save = function(callback) {

//要存入数据库的用户文档

var user = {

name: this.name,

password: this.password,

email: this.email

};

//添加操作

var userModel = new db.Users(user);

userModel.save(function(err,user){

if(err){

console.err(err);

return callback(err);//错误,返回 err 信息

}

console.log("sucess:"+user[0]);

callback(null, user[0]);//成功!err 为 null,并返回存储后的用户文档

});

};

//读取用户信息

User.get = function(name, callback) {

if (name){

db.Users.findOne({"name":name},

function(err,user){

if(err) {

console.err(err);

return callback(err);//失败!返回 err 信息

}

callback(null, user);//成功!返回查询的用户信息

});

}else{

db.Users.find(null,

function(err,users){

if(err) {

console.err(err);

return callback(err);//失败!返回 err 信息

}

callback(null, users);//成功!返回查询的用户信息

});

}

};


图片 6

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

引入上节课创建的mongodb.js文件,将来会使用其导出的类实例。

  1. function User(user) {

this.name = user.name;

this.password = user.password;

this.email = user.email;

};

module.exports = User;

上述代码,利用函数的方式定义了一User类,三个字段:name,password,email,并通module.exports的方式对外导出。

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

.....

}

上述代码,动态的为User类定义了一静态属性,该属性实质是一个方法(function),该方法的作用是将来用于保存用户数据,注册用户时会用到,调用该方法成功后,会向Mongodb数据库的users文档中,插入一条记录,传入的参数:为一回调函数,用于执行成功或失败后的操作。

  1. var userModel = new db.Users(user);

userModel.save(function(err,user){

......

})

上述代码,首先创建mongodb.js文件中导出来Users模型实例,然后,核心的真正调用了mongodb数据库引擎向后台插入数据的代码是:userModel.save(function(err,user){......)
其中:save的两个参数,第一个参数err为如果插入出错,接收服务器返回的错误对象,第二个参数user为如果插入成功,返回操作成功的user用户实例集合。

5. 需要注意的是:如果插入成功,返回的user实际是一个集合,这里由于是插入操作,这个集合里将只有一条记录,即新增的user实例,所以接收时,需要使用user[0]的方式,即:
callback(null, user[0]);//成功!err 为 null,并返回存储后的用户文档

6.对这个User的save方法的调用代码,将来将类似于以下这样:

var newUser = new User({

name: name,  //接收前台传递过来的用户名

password: password, //接收前台传递过来的密码

email: req.body.email  //接收前台传递过来的Email

});

......

newUser.save(function(err, user) {   //调用该User实例的save方法

if (err) {

res.send({ status: 'error', message: "出错了,原因如下:" + err });

return;

}

res.send({ status: 'success', message: "注册成功!", user: user });

});

[javascript]
/**
 * Module dependencies.
 */ 
 
var express = require('express') 
  , routes = require('./routes') 
  , user = require('./routes/user') 
  , http = require('http') 
  , path = require('path') 
  , mongoose = require('mongoose');   //1  
 
 
var app = express(); 
 
 
// all environments  
app.set('port', process.env.PORT || 3000); 
app.set('views', __dirname + '/views'); 
app.set('view engine', 'ejs'); 
app.use(express.favicon()); 
app.use(express.logger('dev')); 
app.use(express.bodyParser()); 
app.use(express.methodOverride()); 
app.use(app.router); 
app.use(express.static(path.join(__dirname, 'public'))); 
 
 
// development only  
if ('development' == app.get('env')) { 
  app.use(express.errorHandler()); 

 
 
app.get('/', routes.index); 
app.get('/log',routes.login); 
app.post('/log',routes.doLogin); 
app.get('/reg',routes.reg); 
app.post('/reg',routes.doReg); 
 
 
//mongoose  
mongoose.connect('mongodb://localhost/test_db');  //2 
 
 
http.createServer(app).listen(app.get('port'), function(){ 
  console.log('Express server listening on port ' + app.get('port')); 
}); 

错误 #6:在回调函数内抛出错误

JavaScript 有个“异常”概念。异常处理与大多数传统语言的语法类似,例如 Java 和 C++,JavaScript 能在 try-catch 块内 “抛出(throw)” 和 捕捉(catch)异常:

JavaScript

function slugifyUsername(username) { if(typeof username === ‘string’) { throw new TypeError(‘expected a string username, got '+(typeof username)) } // ... } try { var usernameSlug = slugifyUsername(username) } catch(e) { console.log(‘Oh no!’) }

1
2
3
4
5
6
7
8
9
10
11
12
function slugifyUsername(username) {
if(typeof username === ‘string’) {
throw new TypeError(‘expected a string username, got '+(typeof username))
}
// ...
}
 
try {
var usernameSlug = slugifyUsername(username)
} catch(e) {
console.log(‘Oh no!’)
}

然而,如果你把 try-catch 放在异步函数内,它会出乎你意料,它并不会执行。例如,如果你想保护一段含有很多异步活动的代码,而且这段代码包含在一个 try-catch 块内,而结果是:它不一定会运行。

JavaScript

try { db.User.get(userId, function(err, user) { if(err) { throw err } // ... usernameSlug = slugifyUsername(user.username) // ... }) } catch(e) { console.log(‘Oh no!’) }

1
2
3
4
5
6
7
8
9
10
11
12
try {
db.User.get(userId, function(err, user) {
if(err) {
throw err
}
// ...
usernameSlug = slugifyUsername(user.username)
// ...
})
} catch(e) {
console.log(‘Oh no!’)
}

如果回调函数 “db.User.get” 异步触发了,虽然作用域里包含的 try-catch 块离开了上下文,仍然能捕捉那些在回调函数的抛出的错误。

这就是 Node.js 中如何处理错误的另外一种方式。另外,有必要遵循所有回调函数的参数(err, …)模式,所有回调函数的第一个参数期待是一个错误对象。

  1. User.get = function(name, callback) {......})

上述代码,动态的为User类定义了一静态属性get,该属性实质是一个方法(function),该方法的作用是用于通过用户名获取用户信息.两个参数,第一个参数name:用户名,第二个参数callback:查询成功或失败后的回调函数。

  1. 用户查找的核心数据库操作代码是:

db.Users.findOne({"name":name},function(err,user){......})
db.Users.find(null,function(err,users){......})
注意findOne与find的区别,一个是查找一个单一用户(不管查到有多少条记录,均只返回第一条记录),所以回调里返回值是user,而 find查找找返回所有符合条件的,所以是个集合users

  1. 将来对User.get方法的调用的客户端代码,将类似于:

//检查用户名是否已经存在

User.get(newUser.name, function(err, user) {

if (err) {

res.send({ status: 'error', message: "出错了,原因如下:" + err });

return;

}

if (user) {

res.send({ status: 'failed', message: "用户已存在!" });

return;

})

/**
 * Module dependencies.
 */

错误 #7:认为数字是整型

数字在 JavaScript 中都是浮点型,JS 没有整型。你可能不能预料到这将是一个问题,因为数大到超出浮点型范围的情况并不常见。

JavaScript

Math.pow(2, 53)+1 === Math.pow(2, 53)

1
Math.pow(2, 53)+1 === Math.pow(2, 53)

不幸的是,在 JavaScript 中,这种关于数字的怪异情况远不止于此。尽管数字都是浮点型,对于下面的表达式,操作符对于整型也能正常运行:

JavaScript

5 >> 1 === 2 // true

1
5 >> 1 === 2 // true

然而,不像算术运算符那样,位操作符和位移操作符只能操作后 32 位,如同 “整型” 数。例如,尝试位移 “Math.pow(2,53)” 1 位,会得到结果 0。尝试与 1 进行按位或运算,得到结果 1。

JavaScript

Math.pow(2, 53) / 2 === Math.pow(2, 52) // true Math.pow(2, 53) >> 1 === 0 // true Math.pow(2, 53) | 1 === 1 // true

1
2
3
Math.pow(2, 53) / 2 === Math.pow(2, 52) // true
Math.pow(2, 53) >> 1 === 0 // true
Math.pow(2, 53) | 1 === 1 // true

你可能很少需要处理很大的数,但如果你真的要处理的话,有很多大整型库能对大型精度数完成重要的数学运算,如  node-bigint。

4.2、新建post.js组件来封装【博客文章】模块功能

继续!在models文件下新建一posts.js文件,代码如下:


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

function Post(name, title, post) {

this.name = name;

this.title = title;

this.post = post;

}

module.exports = Post;

//存储一篇文章及其相关信息

Post.prototype.save = function(callback) {

var date = new Date();

//存储各种时间格式,方便以后扩展

var time = {

date: date,

year : date.getFullYear(),

month : date.getFullYear() + "-" + (date.getMonth() + 1),

day : date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate(),

minute : date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " " +

date.getHours() + ":" + (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes())

}

//要存入数据库的文档

var post = {

name: this.name,

time: time,

title: this.title,

post: this.post  //文章

};

var postModel = new db.Posts(post);

postModel.save(function(err){

if(err){

return callback(err);//错误,返回 err 信息

}

callback(null);//成功!

});

};

//读取文章及其相关信息

Post.get = function(name, callback) {

var query = {};

if (name) {

query.name = name;

}

db.Posts.find(query).sort({time:-1}).find(null,function (err, docs) {

if (err) {

return callback(err);//失败!返回 err

}

callback(null, docs);//成功!以数组形式返回查询的结果

});

}


图片 7

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

引入上节课创建的mongodb.js文件,将来会使用其导出的类实例。

  1. function Post(name, title, post) {

this.name = name;

this.title = title;

this.post = post;

}

module.exports = Post;

上述代码,利用函数的方式定义了一Post类,三个字段:name,title,post,含义与上节课定义的数据模型字段一致,并通module.exports的方式对外导出。

  1. Post.prototype.save = function(callback) {

.....

}

上述代码,动态的为Post类定义了一静态属性,该属性实质是一个方法(function),该方法的作用是将来用于保存用户发表的博文内容,用户发表文章时会用到,调用该方法成功后,会向Mongodb数据库的posts文档中,插入一条记录,传入的参数callback:为一回调函数,用于执行成功或失败后的操作。

  1.  var postModel = new db.Posts(post);

postModel.save(function(err){

......

})

上述代码,首先创建mongodb.js文件中导出来Posts模型实例,然后,核心的真正调用了mongodb数据库引擎向后台插入数据的代码是:postModel.save(function(err){......)

其中:参数err为如果插入出错,接收服务器返回的错误对象,第二个参数省去,因为不需要。

  1. 对这个Post的save方法的调用代码,将来将类似于以下这样:

.....

post = new Post(currentUser.name, req.body.title, req.body.post);

//console.log(post);

post.save(function(err) {

if (err) {

res.send({ status: 'failed', message: "出错了,原因如下:" + err });

} else {

res.send({ status: 'successed', message: "保存成功!" });

}

});

  1. Post.get = function(name, callback) {......})

上述代码,动态的为Post类定义了一静态属性get,该属性实质是一个方法(function),该方法的作用是用于通过用户名获取该用户发表的博文信息.两个参数,第一个参数name:用户名,第二个参数callback:查询成功或失败后的回调函数。

  1. var query = {};

if (name) {

query.name = name;

}

这段代码的意思是,如果从查询参数中获取到name值,则说明要获取某用户的博客列表,如果没有的话,说明要查找所有用户的博客信息

  1. db.Posts.find(query).sort({time:-1}).find(null,function (err, docs) {......})

这段代码是核心的操作数据库查询的代码,根据query传递的值来对posts集合(表)进行查询,如果query为空({}),则说明无条件查询(即查询所有)。sort({time:-1})的意思是对查询的结果按时间倒序排列。find(null,function(err,docs){....})  ,其中的回调函数的第二参数,即用来接收从数据库中查询到符合条件的“博客文章内容”,注意:它是一个集合。

  1. 对Post.get方法调用的客户端示例代码:

Post.get(username, function(err, posts) {

if (err) {

posts = [];

}

res.send(posts);

});

var express = require('express')
  , routes = require('./routes')
  , user = require('./routes/user')
  , http = require('http')
  , path = require('path')
  , mongoose = require('mongoose');   //1

错误 #8:忽略了 Streaming(流) API 的优势

大家都说想建立一个小型代理服务器,它能响应从其它服务器获取内容的请求。作为一个案例,我们将建立一个供应 Gravatar 图像的小型 Web 服务器:

JavaScript

var http = require('http') var crypto = require('crypto') http.createServer() .on('request', function(req, res) { var email = req.url.substr(req.url.lastIndexOf('/')+1) if(!email) { res.writeHead(404) return res.end() } var buf = new Buffer(1024*1024) http.get(''), function(resp) { var size = 0 resp.on('data', function(chunk) { chunk.copy(buf, size) size += chunk.length }) .on('end', function() { res.write(buf.slice(0, size)) res.end() }) }) }) .listen(8080)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var http = require('http')
var crypto = require('crypto')
 
http.createServer()
.on('request', function(req, res) {
var email = req.url.substr(req.url.lastIndexOf('/')+1)
if(!email) {
res.writeHead(404)
return res.end()
}
 
var buf = new Buffer(1024*1024)
http.get('http://www.gravatar.com/avatar/'+crypto.createHash('md5').update(email).digest('hex'), function(resp) {
var size = 0
resp.on('data', function(chunk) {
chunk.copy(buf, size)
size += chunk.length
})
.on('end', function() {
res.write(buf.slice(0, size))
res.end()
})
})
})
.listen(8080)

在这个特殊例子中有一个 Node.js 问题,我们从 Gravatar 获取图像,将它读进缓存区,然后响应请求。这不是一个多么糟糕的问题,因为 Gravatar 返回的图像并不是很大。然而,想象一下,如果我们代理的内容大小有成千上万兆。那就有一个更好的方法了:

JavaScript

http.createServer() .on('request', function(req, res) { var email = req.url.substr(req.url.lastIndexOf('/')+1) if(!email) { res.writeHead(404) return res.end() } http.get(''), function(resp) { resp.pipe(res) }) }) .listen(8080)

1
2
3
4
5
6
7
8
9
10
11
12
13
http.createServer()
.on('request', function(req, res) {
var email = req.url.substr(req.url.lastIndexOf('/')+1)
if(!email) {
res.writeHead(404)
return res.end()
}
 
http.get('http://www.gravatar.com/avatar/'+crypto.createHash('md5').update(email).digest('hex'), function(resp) {
resp.pipe(res)
})
})
.listen(8080)

这里,我们获取图像,并简单地通过管道响应给客户端。绝不需要我们在响应之前,将全部内容读取到缓冲区。

五、后述

本章代码下载:

下章剧透:

《项目实战:基于Angular2+Mongodb+Node技术实现的多用户博客系统教程(9)》

                                                      --  完成Node后台的用户登录模块&加入Session支持

图片 8

var app = express();

错误 #9:把 Console.log 用于调试目的

在 Node.js 中,“console.log” 允许你向控制台打印几乎所有东西。传递一个对象给它,它会以 JavaScript 对象字面量的方式打印出来。它接受任意多个参数,并以空格作为分隔符打印它们。有许多个理由让开发者很想用这个来调试(debug)自己的代码;然而,我强烈建议你避免在真正程序里使用 “console.log” 。你应该避免在全部代码里使用 “console.log” 进行调试(debug),当不需要它们的时候,应注释掉它们。相反,使用专门为调试建立的库,如:debug。

当你开始编写应用程序时,这些库能方便地启动和禁用某行调试(debug)功能。例如,通过不设置 DEBUG 环境变量,能够防止所有调试行被打印到终端。使用它很简单:

JavaScript

// app.js var debug = require(‘debug’)(‘app’) debug(’Hello, %s!’, ‘world’)

1
2
3
// app.js
var debug = require(‘debug’)(‘app’)
debug(’Hello, %s!’, ‘world’)

为了启动调试行,将环境变量 DEBUG 设置为 “app” 或 “*”,就能简单地运行这些代码了:

JavaScript

DEBUG=app node app.js

1
DEBUG=app node app.js

// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

错误 #10:不使用管理程序

不管你的 Node.js 代码运行在生产环境还是本地开发环境,一个监控管理程序能很好地管理你的程序,所以它是一个非常有用并值得拥有的东西。开发者设计和实现现代应用时常常推荐的一个最佳实践是:快速失败,快速迭代。

如果发生一个意料之外的错误,不要试图去处理它,而是让你的程序崩溃,并有个监控者在几秒后重启它。管理程序的好处不止是重启崩溃的程序。这个工具允许你重启崩溃的程序的同时,也允许文件发生改变时重启程序。这让开发 Node.js 程序变成一段更愉快的体验。

有很多 Node.js 可用的管理程序。例如:

  • pm2
  • forever
  • nodemon
  • supervisor

所有这些工具各有优劣。一些有利于在同一个机器里处理多个应用程序,而其它擅长于日志管理。然而,如果你想开始使用这些程序,它们都是很好的选择。

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

总结

正如你所知道的那样,一些 Node.js 问题能对你的程序造成毁灭性打击。而一些则会在你尝试完成最简单的东西时,让你产生挫败感。尽管 Node.js 的开发门槛较低,但它仍然有很容易搞混的地方。从其它编程语言转过来学习 Node.js 开发者可能会遇到这些问题,但这些错误在 Node.js 新手中也是十分常见的。幸运的是,它们很容易避免。我希望这个简短指导能帮助 Node.js 新手写出更优秀的代码,并为我们开发出稳定高效的软件。

打赏支持我翻译更多好文章,谢谢!

打赏译者

app.get('/', routes.index);
app.get('/log',routes.login);
app.post('/log',routes.doLogin);
app.get('/reg',routes.reg);
app.post('/reg',routes.doReg);

打赏支持我翻译更多好文章,谢谢!

任选一种支付方式

图片 9 图片 10

1 赞 7 收藏 1 评论

//mongoose
mongoose.connect('mongodb://localhost/test_db');  //2

关于作者:刘健超-J.c

图片 11

前端,在路上... 个人主页 · 我的文章 · 19 ·     

图片 12

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

4、接下来定义一下Schema和Model,这些就是数据库表的结构。在项目根路径下创建一个models的文件夹,里面添加一个user.js用来定义用户表。models/user.js代码如下。

[javascript]
var mongoose = require('mongoose') 
    , Schema = mongoose.Schema 
    , ObjectId = Schema.ObjectId; 
 
var UserSchema = new Schema({ 
      name: String 
    , password: String 
}); 
 
module.exports = mongoose.model('User', UserSchema); 

var mongoose = require('mongoose')
    , Schema = mongoose.Schema
    , ObjectId = Schema.ObjectId;

var UserSchema = new Schema({
      name: String
    , password: String
});

module.exports = mongoose.model('User', UserSchema);注意最后一行,这里是直接把UserSchema的Model给导出去了,也可以直接导出UserSchema,module.exports = UserSchema,不过这样做的话,再用这个表信息的时候就得单独Model一下。

 

5、引入mongoose后,自己添加相应的文件,在view里新建登录注册页,头部底部这些包含文件也都自己建。先说注册页,表单处理路径为/reg。注意用户名密码的输入框我是直接用name="user[]"这种形式,这样后面可以直接通过这个user来获取相关信息,其实不这样也可以。
[javascript]
<% include header.ejs %> 
<form action="/reg" method="post"> 
<input type="text" name="user[name]" /> 
<input type="password" name="user[password]" /> 
<input type="submit" value="Register" /> 
</form> 
<% include footer.ejs %> 

<% include header.ejs %>
<form action="/reg" method="post">
<input type="text" name="user[name]" />
<input type="password" name="user[password]" />
<input type="submit" value="Register" />
</form>
<% include footer.ejs %>接下来修改doReg方法,这是处理注册事件的函数。
6、修改index.js,因为想在这里注册时候把数据存入到数据库中,所以这里需要引用上面第四步创建的model。具体代码如下所示。

[javascript]
var User = require('../models/user'); 
/*
 * GET home page.
 */ 
 
 
exports.index = function(req, res){ 
  User.find({}, function (err,users) { 
      res.render('index', { title: 'Express',users:users }); 
  }); 
}; 
 
exports.reg=function(req,res){ 
    res.render('reg',{title:'Register Page'}); 
}; 
exports.doReg=function(req,res){ 
    var user = new User(req.body.user); 
    user.save(function (err, user) { 
        if(!err) { 
            console.log(user); 
            res.redirect('/') 
        } 
    }); 
    console.log(req.body.user); 
}; 

var User = require('../models/user');
/*
 * GET home page.
 */

exports.index = function(req, res){
  User.find({}, function (err,users) {
      res.render('index', { title: 'Express',users:users });
  });
};

exports.reg=function(req,res){
    res.render('reg',{title:'Register Page'});
};
exports.doReg=function(req,res){
    var user = new User(req.body.user);
    user.save(function (err, user) {
        if(!err) {
            console.log(user);
            res.redirect('/')
        }
    });
    console.log(req.body.user);
};这一步要注意为什么是req.body.user,这里就是前面写页面直接用user[]这种形式带来的好处,如果单独写name="username",那么这里就应该是下面这种形式。
[javascript]
var user = new User({ 
    name:req.body['username'], 
    password:req.body['password'] 
}); 

var user = new User({
 name:req.body['username'],
 password:req.body['password']
});user.save就是把刚才数据提交到数据库,具体使用方法参看官方文档。save成功之后执行什么操作就自己发挥吧。保存之前也可以进行一些简单的表单验证等等。

7、通过登录来讲如何从数据库取出数据,继续修改index.js,如下所示。

[javascript]
var User = require('../models/user'); 
/*
 * GET home page.
 */ 
 
exports.index = function(req, res){ 
  User.find({}, function (err,users) { 
      res.render('index', { title: 'Express',users:users }); 
  }); 
}; 
exports.login=function(req,res){ 
    res.render('log',{title:'Login Page'}); 
} ; 
exports.doLogin=function(req,res){ 
    var user = req.body.user; 
    User.find(user,function(err,docs){ 
        if(!err){ 
            if(docs!=''){ 
                console.log(docs);                 
                return res.redirect('/'); 
            } else{ 
                console.log('用户名或密码不正确'); 
                return res.redirect('/log'); 
            } 
 
        }else{ 
            console.log("Something happend."); 
        } 
    }) 
}; 
exports.reg=function(req,res){ 
    res.render('reg',{title:'Register Page'}); 
}; 
exports.doReg=function(req,res){ 
    var user = new User(req.body.user); 
    user.save(function (err, user) { 
        if(!err) { 
            console.log(user); 
            res.redirect('/') 
        } 
    }); 
    console.log(req.body.user); 
}; 

var User = require('../models/user');
/*
 * GET home page.
 */

exports.index = function(req, res){
  User.find({}, function (err,users) {
      res.render('index', { title: 'Express',users:users });
  });
};
exports.login=function(req,res){
    res.render('log',{title:'Login Page'});
} ;
exports.doLogin=function(req,res){
    var user = req.body.user;
    User.find(user,function(err,docs){
        if(!err){
            if(docs!=''){
                console.log(docs);               
                return res.redirect('/');
            } else{
                console.log('用户名或密码不正确');
                return res.redirect('/log');
            }

        }else{
            console.log("Something happend.");
        }
    })
};
exports.reg=function(req,res){
    res.render('reg',{title:'Register Page'});
};
exports.doReg=function(req,res){
    var user = new User(req.body.user);
    user.save(function (err, user) {
        if(!err) {
            console.log(user);
            res.redirect('/')
        }
    });
    console.log(req.body.user);
};
这里通过find()方法来查询数据库,使用方法不解释了。第一个参数user就是要查询的数据,从输入框获取过来的,如果不是用user[]这种形式定义的name属性,那么这里一样的用{naem:req.body['username'],password:req.body['password']}这样的写法。回调函数docs就是从数据库查询返回的结果。

 

例子到此结束。

 

npm install mongoose 2、连接mongodb数据库,在app.js里面添加如下两行代码。 [javascript] var...

本文由9159.com发布于前端,转载请注明出处:利用mongoose第三方库进行业务数据操作, 1、开始

关键词: