完成了模块引入加载和一些基本的设置,现在来编写用户注册的逻辑代码,上面说到对于路径/的访问处理在routes模块中,这个模块指的就是routes文件夹下面的index.js,部分代码如下:
var express = require('express'); var crypto = require('crypto'); var router = express.Router(); var db=require('../db'); var User=require('../models/user'); var Post=require('../models/post'); /* GET home page. */ router.get('https://www.jb51.net/', function(req, res, next) { Post.find({},function(err,posts){ if(err){ req.session.message=err.message; return res.redirect('https://www.jb51.net/'); } res.render('index',{ posts:posts }); }); }); //发表微博 router.post('/post',function(req, res, next){ var currentUser=req.session.user; var post=new Post({ user:currentUser.name, post:req.body.article, updated:getTime(new Date()) }); post.save(function(err){ if(err){ req.session.message=err.message; return res.redirect('/reg'); } req.session.success="发表成功"; res.redirect('/users/'+currentUser.name); }); }); function getTime(date){ return date.getFullYear()+ "-"+date.getMonth()+1+"-"+ date.getDate()+" "+ date.getHours()+":"+ date.getMinutes(); } router.get('/reg', isLogin); //用户进入注册页面 router.get('/reg',function(req,res){ res.render('reg',{title:"用户注册"}); }); router.post('/reg', isLogin); //用户点击注册按钮 router.post('/reg',function(req,res){ if(req.body['password']!= req.body['passwordconf']){ req.session.error="两次密码不一致"; return res.redirect('/reg'); } var md5=crypto.createHash('md5'); var password=md5.update(req.body.password).digest('base64'); var newUser=new User({ name:req.body['username'], password:password }); User.findOne({name:newUser.name},function(err,user){ if(user){ err="用户名已经存在"; } if(err){ req.session.error=err; return res.redirect('/reg'); } newUser.save(function(err){ if(err){ req.session.error=err.message; return res.redirect('/reg'); } req.session.user=newUser; req.session.success="注册成功"; res.redirect('https://www.jb51.net/'); }); }); }); router.get('/login',isLogin); router.get('/login',function(req,res){ res.render('login',{title:"用户登陆"}); }); router.post('/login',isLogin); router.post('/login',function(req,res){ var md5=crypto.createHash('md5'); var password=md5.update(req.body.password).digest('base64'); User.findOne({name:req.body.username},function(err,user){ if(!user){ req.session.error="用户不存在"; return res.redirect('/login'); } if(user.password!=password){ req.session.error="密码错误"; return res.redirect('/login'); } req.session.user=user; req.session.success="登录成功"; res.redirect('https://www.jb51.net/'); }); }); router.get('/logout',function(req,res){ req.session.user=null; res.redirect('https://www.jb51.net/'); }); function isLogin(req,res,next){ if(req.session.user){ req.session.message="用户已登录"; return res.redirect('https://www.jb51.net/'); } next(); } module.exports = router;
上述代码1-6行都是对外部模块的引入,8-19行是对首页路由/的处理代码。117行将该模块定义为router供外部调用。我们主要看54-83行,这些代码就是用户注册的代码,54行监听来自用户对于/reg路由的post请求,首先判断两次密码是否一致,如果不一致在session中存储一个错误信息然后跳转到到当前页面显示错误信息,该错误信息供模板引擎显示给用户。如果两次密码一致首先对密码进行md5加密,使用的是nodejs提供的核心模块crypto,并生成一个对象模型User,该对象模型是mongoose中提供的一个model的实例,mongoose在它上面定义了一些操作数据库的方法。然后调用这个实例的findOne方法检测该用户是否已经存在,如果存在就保存错误信息到session并跳转到当前页显示错误。如果不存在这样一个用户就使用save方法进行用户信息保存,注册成功后将用户信息保存在session中,并保存一个success的提示信息,然后跳转到首页。这里需要注意一个坑,以前做php或者.net的时候,我们通常都是先查询数据库等数据库返回结果提示用户是否存在之后再进行用户的save然后在跳转,这是一种同步方式,跳转操作需要等待findOne操作返回结果之后才能进行。而nodejs中采用异步IO,最后的跳转操作需要放在findOne操作的回调函数中进行,跳转操作不必等待findone操作结束后执行,两者是异步的。如果将最后的redirect操作放在findOne操作外部而不是回调函数中,你会在控制台上得到一个Can't set headers after they are sent的错误,这是因为在fineOne以及save操作之前已经进行行了跳转,response响应已经结束,不能够重复响应请求。