NodeJs ·

NodeJS记 – 用户注册登录功能的实现

NodeJS 记 – Express框架使用

点击查看 开发一个程序就不离不开各种框架,框架的好处是让我们能更专注业务逻辑的处理,提高开发效率。但同时也有坏处,如: 代码不可控,有坑不知道在哪里,更别说去填。 导致项目臃肿,框架可能集合···

继上次我们简单的阐述了Express的使用,今天我们将完成用户注册和登录的功能。我们使用mysql数据库来存储用户数据,当然你也可以使用其他的数据存储工具。下载安装Mysql我们就略过了。安装成功后我就创建一个User表,我们可以使用命令和视图化连接工具去创建User表。我一直是用命令的方式去创建,这得我觉得有下面这些好处:

  • 能使我们更加的熟练SQL语句。
  • 当你熟悉SQL后,创建的速度绝对比使用可视化工具效率高。
  • 也可以学习另一种操作数据的方式。

打开用管理者的身份运行CMD,为什么要管理者的身份运行呢?因为在某些情况下,我的数据库在windows7中有可能会GG,因此有些操作需要管理权限才能操作, cd到mysql的bin目下:执行命令

C:\Program Files\MySQL\MySQL Server 5.6bin>

可以看到连接mysql异常,查看下mysql的service是否运行正常,打开windows系统services发现mysql服务不存在,在命令中执行:mysqld --install命令重新安装服务,然后刷新windows服务,mysql服务出现了,但是还没有启动,右击mysql,点击start服务。然后再连接mysql成功。

  • show databases 是显示mysql中有哪些数据库
  • use bpdb 是我们需要使用的数据库
  • show tables 是显示数据库的表
  • describe user 是查看表结构

命令行必须要以分号英文分号;结尾。在创建数据中字段要用``(英文输入法在Esc下面)包裹,要不然会报错。可以看到我们已经创建了一个User表。好了现在我们准备写代码了。首先我们要用的一个开源的mysql连接库github地址:https://github.com/mysqljs/mysql 。我在package.json中加入库。

以英文逗号分开json格式,后面是版本号。然后我们在目下下执行npm install就会自动安装mysql的连接驱动库。在github的地址中有很多关于这个库的用法介绍。我们使用其中的数据库连接池的用法为事列。创建一个db文件夹,创建一个db.js文件,码代码:

var mysql = require('mysql');
//创建一个数据库连接池
var pool = mysql.createPool({
	host : 'localhost',//host
	user : 'root',//y用户
	//password : '',数据库的密码
	database : 'bpdb'//数据库的名字
});

function query(sql,callback){
	//获取一个连接
	pool.getConnection(function (error,connection){
		if (error) {
			console.log("db error:"+error);
			return;
			}		
		console.log("connected");
		connection.query(sql,function(error,resluts){
			//释放连接并且返回查询结果
			connection.release();
			callback(error,resluts);
			
		});

	});
}
//导出模块
exports.query = query;

这样在别的文件中就可以require到这个模块了。新建一个controller文件夹并且创建user.js文件。

'use strict';//使用严苛模式
var db = require('../db/db.js');//引用msyql模块
//var fs = require('fs');
//var path = require('path');
var jwt = require('jwt-simple');//引入token生成模块
module.exports = {
	
	
	//user login
	 login : function(req,res,next){
	 	var name = req.body.name;
	 	var password = req.body.password;
	 	//var isPhone = req.body.isPhone;
	 	var sql = 'select * from user where name = ' + "'" + name + "'" + ' and password = '+ "'" + password + "'";
	 	console.log("login sql::"+sql);
	 	db.query(sql,function(err,results){
	 		if(err){
	 			let error = {code : 103,message : err};
	 			res.status(200).end(JSON.stringify(error));
	 			//console.log("error::"+err);
	 			return;
	 		}
	 		//console.log("login sql::"+JSON.stringify(results));
	 		if(results.length !== 0){
	 			let isAdmin = results[0].role == 0 ? false : true;
	 			console.log('role  ::'+results[0].role);
	 			var payload = {
	 				id : results[0].id,
	 				name : results[0].name,
	 				admin : isAdmin
	 			};
	 			var secret = 'secret';
	 			var token = jwt.encode(payload, secret);
	 			//console.log('token ::'+token); 			
	 			res.header("token",token);
	 			//if(isPhone !== undefined && isPhone){
		 			var result = {code : 100 ,message : 'login success',user : results};
		 			res.status(200).end(JSON.stringify(result));
	 			//}
	 			//else{
	 				//renderHtml(req,res);
	 			//}
	 		}else{
	 			var result = {code : 105,message : 'login failed,not found user'};
	 			res.status(200).end(JSON.stringify(result));
	 		}

	 	});
	},

	//user sigonout
	signout : function(req,res,next){

	},
	//user register
	register : function(req,res,next){
		
	 	var user = {
	 		name : '',
	 		password : '',
	 		email : '',
	 		sex : 0,
	 		heigh : 0,
	 		weight : 0,
	 		avatar : '',
	 		age : 0,
	 		role : 0 // 0 is common person, 1 is doctor,

	 	};
	 	user.name = req.body.name;
	 	user.password = req.body.password;
	 	console.log("user.name::"+user.name);
	 	if (typeof(user.name) == 'undefined' || user.name == null || user.name.length == 0) {
	 		let result  = {code : 102,message:'invalid username'};
	 		res.status(200).end(JSON.stringify(result));
	 		return;
	 	}
	 	if (typeof(user.password) == 'undefined' || user.password == null || user.password.length == 0) {
	 		let result  = {code : 102,message:'invalid password'};
	 		res.status(200).end(JSON.stringify(result));
	 		return;
	 	}

	 	//var sql = 'select * from `user` where `username` = "john"';
	 	var sql = 'select * from user where name = ' + "'"+ user.name + "'";
	 	//console.log("sql::"+sql);
	 	db.query(sql,function(err,resluts){
	 		if(err){
	 			let error = {code : 103,message : err};
	 			res.status(200).end(JSON.stringify(error));
	 			//console.log("error::"+err);
	 			return;
	 		}else{	 			
	 			if (resluts.length == 0) {
	 				user.email = req.body.email;
	 				user.sex = req.body.sex;
	 				user.heigh = req.body.heigh;
	 				user.weight = req.body.weight;
	 				user.age = req.body.age;
	 				user.avatar = req.body.avatar;
	 				user.role = req.body.role || 0;
	 				var insertSql = 'insert into user (name,password,email,sex,heigh,weight,age,avatar,role) values ('+ "'"+user.name + "',"+ 
	 				"'" + user.password +"',"+ "'"+user.email + "',"+ user.sex +','+user.heigh + ',' +user.weight +',' + user.age + ',' + "'" + user.avatar + "'" +','+user.role+ ')';
	 				//console.log('insert sql::'+insertSql);
	 				db.query(insertSql,function(err,results){
	 					if(err){
	 						let error = {code : 103,message : err};
	 						res.status(200).end(JSON.stringify(error));
	 						return;
	 					}		 					
	 					//console.log('results.insertId::'+results.insertId );
	 					if(typeof(results.insertId) !== undefined){
		 					let register = {code : 100,message : 'register success',user_id : results.insertId};
		 					res.status(200).end(JSON.stringify(register));
	 					}
	 				});
	 			}else{
	 				let exist = {code : 101,message : 'user name was already exist'};
	 				res.status(200).end(JSON.stringify(exist));
	 			}	 			
	 		}
	 	});

	},

上面是user登录和注册的函数,并且我们module.exports导出这两个函数。

登录的实现:函数中首先我们利用前端传递过来的数据用户name和password的字段去查询数据库,如果查询出错就利用返回给前端,如果没有此用户我们就提醒前端没有找到。你也可以进一步去细化下,如用户名输入错误,密码输入错误等等。查询完成后我们会用jwt-simple去生成一个token令牌,以后用户每次请求都会携带此令牌,我们验证它的身份。关于jwt-siplem我们会在下次token,cookie,session,http章节详细阐述,此处略过。

注册的实现:首先获取前端传递过来的所有用户字段并且赋值,检查用户的的用户名和密码是否有效,然后查询数据库是否已存在此用户,如果没有我们就添加此用户到数据库。

  • 特别说明:因为查询数据是异步的,返回的结果在function(err,resluts)的results中,所以我们注册的时候,插入数据的必须在查询结果回调的函数中执行,这也是非堵塞I/O的实现,可以看出如果我们要继续在插入数据后再执行其他的逻辑,就会导致函数回调很多,代码难看,逻辑不好理解。
  • 这也是非堵塞I/O带来的一个弊端。在java中我们只有等查询执行完返回然后在执行插入的代码就好了,java中的这种代码非常符合人们的思维逻辑。真的好的做法应该是他们两的结合。
  • 用java的逻辑去写代码,代码的执行是却是非堵塞I/O的。好像Go语言就是这样的。当然避免这些大量回调的写法也是可以通过一些中间件来出来的。而且可以看出上面的代码没有MVC的思想,应该把user独立出一个模块。后面我们会用到ORM工具来处理这些问题。目前先实现功能。

user登录注册已写好,现在添加它的路由。这说道路由,看过Express的朋友应该知道,express有两种路由实现一种是:

var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.get('/', index);
app.get('/users', users);
还有一种是使用exprees.Router():

router.use(function(req, res, next) {
  // .. some logic here .. like any other middleware
  next();
});
// will handle any request that ends in /events
// depends on where the router is "use()'d"
router.get('/events', function(req, res, next) {
  // ..
});

开始我不是很理解作者的为啥要搞两个这样的用法,导致我不知道用那种的好,网上有的用第一个,有的用第二个。后来我写了几次代码发现用第二种的好。第一种写法是Express 4.0之前的写法,第二站是4.0后的写法。至于为啥第二种路由方式好呢?我的思考是:

如果你的url请求很多,你就有两个写法。第一种:

app.post('/user/signOut', function (req, res) {
  res.send('POST request to homepage');
});
app.get('/user/:id', function (req, res) {
  res.send('POST request to homepage');
});
app.post('/user', function (req, res) {
  res.send('POST request to homepage');
});
app.post('/user/login', function (req, res) {
  res.send('POST request to homepage');
});app.post('/user/register', function (req, res) {
  res.send('POST request to homepage');
});

可以看出如果URL躲起来app.js文件到处是这种写法。代码难看,沉余代码多。后面有问题排错难。

第二种写法:就是把各种请求分类写到一个文件中,比如User请求就建立一个userRouter.js,其他的就xxRouter.js但是你得把app export出去。虽然实现了mvc但是app到处被引用,耦合太高就容易出错,并且如果写单元测试每次都要引入一个app。

第三种写法:

var user = require('userRouter');  
app.use('/user',user);

这样userRouter.js:

function userRouter(req,res,next){
    //处理user请求的逻辑,解析不同的url
}
export.user = userRouter;

虽然比第一种好,比第二中不用引用app。但是解析url会出现很多的判断语句,也很容易出错。

上面这戏缺陷在Express4.0中引入router中间件可以解决了,让我们一起来看router是如何解决的。首先我们在app.js中

//路由中间件
var index = 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('/', index);
app.use('/users', users);

在router文件夹下有index.js和user.js模块,我们暂时不管index.js先看user.js代码

var User =require('../controller/user.js')
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.post('/register',User.register);
router.post('/login',User.login);
//为后面用的router
//router.get('/getAllUser',User.getAllUser);
//router.get('/getUser',User.getUser);
//router.post('/delete',User.deleteUser);
//router.post('/update',User.updateUser);
module.exports = router;

引入user.js和express,然后获取express.Router。再使用router分别把register和login的请求url和user.js中的register和login函数联系起来。每当请求register和login就会调用user.js 中注册和登录的方法。注册和登录的后台代码写好了。我们一起测试下。

我下载了postMan第三方模拟http各种请求的工具。首先我们启动server。然后用postman模拟注册。在测试过程中我的数据库服务又死了。如果msyql服务GG了,我们在bin目录下执行:mysqld --remove,mysqld --install。然后打开services重启服务就好了。

postMan中模拟数据

send后获取返回的数据:

去数据中查询下是否真的成功了。

发现user是在里面的。每次操作数据返回的result是一个数组对象,对象里面有些搜吗内容呢?我们打印看看

可以看到注册的时候select sql查询出来是空数组对象,插入的结果就是insert result打印的那些字段了。我们判断inserId是否有值就知道成功与否。然后我们再模拟登陆。

模拟登陆也是成功的。看下查询的时候数据库的results.

是个user的 json数组。至此我们注册和登录功能server端暂时完成了。注册的前端就不说了和登录一样的,我们就以登录的前端作为案例。登录页面

<form   autocomplete="on"   onsubmit="return login()"> 
        <h1>Log in</h1> 
         <p> 
        <label for="username" class="uname" data-icon="u" > Your email or username </label>
        <input id="username" name="name" required="required" type="text" placeholder="myusername or mymail@mail.com"/>
        </p>
        <p> 
        <label for="password" class="youpasswd" data-icon="p"> Your password </label>
        <input id="password" name="password" required="required" type="password" placeholder="eg. X8df!90EO" /> 
        </p>
        <p class="login button"> 
        <input type="submit"  value="Login" id="send" />
		</p>
    </form>

我们使用form提交表单。注意form提交的时候会触发form的onsubmit的事件,我们在onsubmit中return了一个login()函数。

function login() {
            console.log('login ..............');
            //ajax 请求
            $.ajax({
                type: "post",//请求方式
                url: "http://localhost:8001/user/login",//请求url
                data: $("form").serialize(),//请求数据
                async: true,//请求是否异步
                dataType: "json",//请求数据的格式
            })
            //请求成功返回
            .done(function(result,textStatus,xhr) {
                localStorage.setItem('bloodToken',xhr.getResponseHeader('token'));
                 console.log("error_______"+JSON.stringify(result));

                console.log("error::::"+localStorage.getItem('bloodToken'));
                console.log("error::::"+result.user[0].role);
                localStorage.setItem('admin',result.user[0].role);
                localStorage.setItem('userID',result.user[0].id);
                window.location.href = 'static/index.html';
                //window.close();
            })
            .fail(function() {//请求失败返回
                console.log("error");
            })
            
                //防止表单重复提交 返回false
                return false;
        }

login我们采用的ajax请求,什么是ajax请求呢?详情请访问:https://baike.baidu.com/item/ajax/8425?fr=aladdin

到此我们的用户登录和注册功能算是完成了。上面这个案例还有些不足之处:如密码没有加密,token没有设置过期时间,没有验证码这些,但是登录的和注册的过程是大同小异。我们下次将使用ORM工具优化server端并且对http,session,cookie,token的基础进行阐述。

NodeJS记 – 开启nodejs之旅

点击查看 之前一直从事Android方面的工作,负责公司产品的应用层app和修改framwork的工作。从一个月前开始从事后端开发,本来可以选择java做为后端开发的,对java比较熟悉,得···

NodeJS 记 – Express框架使用

点击查看 开发一个程序就不离不开各种框架,框架的好处是让我们能更专注业务逻辑的处理,提高开发效率。但同时也有坏处,如: 代码不可控,有坑不知道在哪里,更别说去填。 导致项目臃肿,框架可能集合···

参与评论