`

node.js学习入门示例

阅读更多
  这些天有点儿空闲,学习了一下node.js。先把《Node.js开发指南》书中的博客例子调通了,费了挺大的劲儿,因为好多插件出了新的版本,而新版本中有的函数、方法又失效了。
  自已又练习着做了一个例子,个人记账系统。主要是想在手机上用,所以界面做得很简单。
  解决了以下一些问题:express使用、bootstrap排版布局、mongodb模糊查询、mongodb统计(group/mapReduce)、session处理、req.flash方法使用等、路由设置等。花了不少的心思。
  例子中用到的插件的版本:
  connect-flash@0.1.1
  connect-mongo@0.4.0
  ejs@0.8.5
  express@3.4.8
  mongodb@1.3.23

  Bootstrap V3.0.3

  贴几张系统运行的图片,有图有真相嘛。













  核心代码贴一下:
一、app.js

/**
 * Module dependencies.
 */

var express = require('express');
var http = require('http');
var path = require('path');
var util=require('util');

var routes = require('./routes');
//var User = require('./modules/user.js');	
var settings=require('./Settings');

var MongoStore = require('connect-mongo')(express);
var flash = require('connect-flash');

var app = express();

//app.set('appTitle','老王个人记账系统');
app.locals.gAppTitle = settings.appName;
//这个没有使用
app.locals.gPageSize = settings.pageSize;
// all environments
app.set('port', process.env.PORT || 8484);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.bodyParser());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(express.cookieParser());
app.use(flash());
app.use(express.session({
	"secret":settings.cookieSecret,
	"store":new MongoStore({
		db:settings.db
	})
}));

app.use(function(req, res, next){
  //跟踪;
  //console.log("req.method="+req.method);
  //console.log("req.url="+req.url);
  //console.log("req.originalUrl="+req.originalUrl);
  var url = req.originalUrl;
  //简单地定义一个登录拦截器
  if ((url == "/month" || url=="/stat" || url=='/list' || url=='/record') && !req.session.user) {
  	  console.log("登录拦截器提示:必须登录,才能执行此项操作。");
  	  req.flash('error', '请先登录。');
      return res.redirect("/login");
  }
  
  res.locals.user = req.session.user;
  
  var error = req.flash('error');
  res.locals.error = error.length?error:null;
  //console.log("转移flash中的error值:"+error);
  
  var success = req.flash('success');
  res.locals.success = success.length?success:null;
  //console.log("转移flash中的success值:"+success);
  
  res.locals.session = req.session;
  next();
});

app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
//console.log(util.inspect(app));

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

//console.log('注册路由.');

routes(app);


http.createServer(app).listen(app.get('port'), function(){
	console.log();
	console.log();
	console.log('/**************************************************/');
	console.log('/*  我的第一个NODE.JS例子。BY 隔壁老王 2014-3-29  */');
	console.log('/*   欢迎访问我的博客:http://wallimn.iteye.com   */');
	console.log('/**************************************************/');
  	console.log('============服务启动成功,监听端口:' + app.get('port')+"============");
});


二、路由处理(routes\index.js)
var crypto = require('crypto');
var User = require('../modules/user.js');	
var Consume = require('../modules/consume.js');
/*
 * GET home page.
 */

module.exports = function(app) {
	app.get('/',function(req, res){
	  res.render('index', { title: '首页' });
	  /*
		res.render('login',{
			title:'用户登录',
		});
	  */
	  //res.redirect('/login');
	});

	
	app.get('/record',function(req,res){
		var user = req.session.user;
		if(!user){
			req.flash('error', '用户未登录,请登录。');
			return res.redirect('/login');
		}

		Consume.get(user.name,{limit:16}, function(err, records) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			res.render('record', {
				title: user.name,
				consumes: records,
			});
		});
	});
	
	//预处理,如果通过,再进行下一个。
	app.post('/record',checkLogin);

	app.post('/record', function(req, res) {
		var currentUser = req.session.user;
		var record = new Consume();
		record.loadFromReq(currentUser.name, req.body);
		record.save(function(err) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/');
			}
			req.flash('success', '发表成功');
			res.redirect('/record');
		});
	});

	//这个应该去掉。留在这里当个后门吧
	//可以查看其他用户的数据
	app.get('/u/:user', function(req, res) {
		var username = req.params.user;
		if (!username) {
			req.flash('error', '未指定用户');
			return res.redirect('/error');
		}

		Consume.get(username,{limit:0}, function(err, records) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			console.log(records);
			res.render('list', {
				title: username,
				consumes: records,
			});
		});
	});
	
	app.get('/del/:id', function(req, res) {
		var id = req.params.id;
		if (!id) {
			req.flash('error', '未指定要删除的记录ID');
			return res.redirect('/error');
		}
		
		console.log("准备删除记账记录,_id="+id);
		Consume.del(id, function(err, records) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			res.redirect('/record');;
		});
	});
	
	app.get('/logout',function(req,res){
		req.session.user = null;
		req.flash('success','登出成功');
		res.redirect('/login');
	});
	
	app.get('/list',function(req,res){
		var user = req.session.user;
		if(!user){
				req.flash('error', "您没有登录,请登录。");
				console.log("没有登录,重定向的登录界面。");
				return res.redirect('/login');
		}
		Consume.get(user.name,{limit:0}, function(err, records) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			res.render('list', {
				title: user.name,
				consumes: records,
			});
		});
	});
	
	app.post('/search',function(req,res){
		var user = req.session.user;
		if(!user){
				req.flash('error', "您没有登录,请登录。");
				return res.redirect('/login');
		}
		var keyword = req.body.keyword;
		console.log("搜索关键字:"+keyword);
		Consume.get(user.name,{limit:0,keyword:keyword}, function(err, records) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			res.render('record', {
				title: user.name,
				consumes: records,
			});
		});
	});
	
	app.get('/stat',function(req,res){
		var user = req.session.user;
		Consume.stat(user.name, function(err, results) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			res.render('stat', {
				title: user.name,
				results: results,
			});
		});
	});
	
	app.get('/month',function(req,res){
		var user = req.session.user;
		Consume.month(user.name, function(err, results) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			res.render('stat', {
				title: user.name,
				results: results,
			});
		});
	});
	
	app.get('/error',function(req,res){
		res.render('error');
	});

	//处理用户登录。
	app.post('/login',function(req,res){
		var md5=crypto.createHash('md5');
		var password = md5.update(req.body.password).digest('hex');
		
		User.get(req.body.username,function(err,user){
			if(!user){
				req.flash('error','用户不存在');
				return res.redirect('/login');
			}
			
			if(user.password!=password){
				req.flash('error','用户口令错误');
				return res.redirect('/login');
			}
			
			req.session.user = user;
			req.flash('success','登入成功');
			//res.redirect('/');
			res.redirect('/record');
		});
	});

	app.get('/login',function(req,res){
		res.render('login',{
			title:'用户登录',
		});
	});

	app.get('/reg',function(req,res){
		
		res.render('reg',{
			title:'用户注册'
		});
	});

	app.post('/reg',function(req,res){
		if(req.body['password-repeat']!=req.body['password']){
			req.flash('error','两次输入的口令不一致!');
			return res.redirect('/reg');
		}
		
		var md5=crypto.createHash('md5');
		var password = md5.update(req.body.password).digest('hex');
		
		var newUser = new User({
			name:req.body.username,
			password:password,
		});
		
		User.get(newUser.name,function(err,user){
			if(user)
				err='同名用户已经存在,请更换名字.';
			if(err){
				req.flash('error',err);
				return res.redirect('/reg');
			}
			
			newUser.save(function(err){
				if(err){
					req.error=err;
					return res.redirect('/reg');
				}	
				req.session.user = newUser;
				req.flash('success','注册成功!');
				res.redirect('/record');
			});
		});
	});

	//测试函数
	app.get('/hello',function(req,res){
		res.send('The time is '+new Date().toString());
	});

	//测试函数
	app.get('/sayhello',function(req,res){
		res.send('hello '+req.params.username);
	});
	
};

//检查是否登入.
function checkLogin(req, res, next) {
	if (!req.session.user) {
		req.flash('error', '尚未登录,无法操作。');
		return res.redirect('/error');
	}
	next();
}

function checkNotLogin(req, res, next) {
	if (req.session.user) {
		req.flash('error', '已登入');
		return res.redirect('/');
	}
	next();
}


三、消费数据处理(modules\consume.js)
var mongodb = require('./db');
var BSON = require('mongodb').BSONPure;
var util=require('util');


function toObjectId(id){
	console.log("转换值:"+id);
	if( id=="" || id=="null" || id=="undefined" || id==undefined || id==null)return null;
	return BSON.ObjectID.createFromHexString(id);
}

function Consume(username, consumeDate,consumeSubject,consumeAmount,consumeRemark, time)
{
	//加载时要单独赋值
	this._id=null;
	
    this.userName = username;
    this.consumeDate = consumeDate;
    this.consumeSubject = consumeSubject;
    this.consumeAmount = consumeAmount;
    this.consumeRemark = consumeRemark;

    if (time)
    {
        this.time = time;
    }
    else
    {
        this.time = new Date();
    }
    
};
module.exports = Consume;

Consume.prototype.loadFromReq = function loadFromReq(username,reqBody,time){
	//自动进行了ID类型的转换。
	this._id = toObjectId(reqBody._id);
    this.userName = username;
    this.consumeDate = reqBody.consumeDate;
    this.consumeSubject = reqBody.consumeSubject;
    this.consumeAmount = reqBody.consumeAmount;
    this.consumeRemark = reqBody.consumeRemark;

    if (time)
    {
        this.time = time;
    }
    else
    {
        this.time = new Date();
    }
}

Consume.prototype.save = function save(callback)
{
    // 存入 Mongodb 的文档
    var record = 
    {
    	_id:this._id,
        userName: this.userName, 
        consumeDate: this.consumeDate, 
        consumeSubject: this.consumeSubject, 
        consumeAmount: this.consumeAmount, 
        consumeRemark: this.consumeRemark, 
        time: this.time, 
    };
    console.log('保存,记录日期:'+record.consumeDate);
    mongodb.open(function(err, db){
        if (err)
        {
            return callback(err); 
        }
        // 读取 posts 集合
        db.collection('consume', function(err, collection){
            if (err)
            {
                mongodb.close(); return callback(err); 
            }

            // 插入
            /*
            collection.insert(record, {safe: true} , function(err, post){
                mongodb.close(); callback(err, post); 
            }); 
            */
           
           console.log("插入或更新,判断依据_id="+record._id);
		   if(record._id==null){
		   	   delete record._id;
		   	   console.log("删除_id,record._id="+record._id);
		   }
           collection.update({_id:(record._id?record._id:'no-record')}, record, {upsert:true,multi:false} , function(err, post){
                mongodb.close(); callback(err, post); 
            }); 
        }); 
    });
};

//删除方法
Consume.del = function del(id,callback){
	mongodb.open(function(err,db){
        if (err){
            return callback(err); 
        }
        
		var query = {_id:BSON.ObjectID.createFromHexString(id)};
		
        db.collection('consume', function(err, collection){
            if (err){
                mongodb.close(); 
                return callback(err); 
            }

			collection.remove(query,{safe:true},function(err,result){
				mongodb.close();
		        if (err){
		            return callback(err); 
		        }
		        console.log("删除成功。");
				callback(null); 
		    }) ;
		});    
   	});
};



Consume.get = function get(username,options, callback)
{
    mongodb.open(function(err, db){
        if (err){
            return callback(err); 
        }
        // 读取 posts 集合
        db.collection('consume', function(err, collection){
            if (err){
                mongodb.close();
                return callback(err); 
            }

            var query = {};
            if(options.keyword){
            	//var regx = new RegExp("/"+options.keyword+"/");
            	//注意,不用/
            	var regx = new RegExp(options.keyword);
            	//限制用户名,科目或者金额与输入关键相等
				query={"$and":[{userName:username},
							   {"$or":[{consumeSubject:regx},
									   {consumeDate:regx},
							   		   {consumeAmount:options.keyword}
									  ]
							   }]
					   };
            }
            else{
                query.userName = username; 
            }
            console.log("搜索条件:");
            console.log(query);
            if(!options.limit){
            	options.limit=0;
            }
            collection.find(query).sort({consumeDate:-1 }).limit(options.limit).toArray(function(err, docs){
                mongodb.close(); 
                if (err){
                    callback(err, null); 
                }

                var consumes = []; 
                docs.forEach(function(doc, index){
                    var record = new Consume(doc.userName, doc.consumeDate,doc.consumeSubject,doc.consumeAmount,doc.consumeRemark, doc.time);
                 	record._id = doc._id;
                    consumes.push(record); 
                }); 
                callback(null, consumes); 
            }); 
        }); 
    });
};



Consume.stat = function stat(username, callback)
{
    mongodb.open(function(err, db){
        if (err){
            return callback(err); 
        }
        // 读取 posts 集合
        db.collection('consume', function(err, collection){
            if (err){
                mongodb.close();
                return callback(err); 
            }

			var reduce = function(obj,prev){
					prev.amount += isNaN(obj.consumeAmount)?0:Number(obj.consumeAmount);
					prev.count++;
			};
		
            collection.group(
            	[ 'consumeSubject' ],
        		{userName:username},
        		{count:0,amount:0},
            	reduce,
            	function(err, result){
                	mongodb.close(); 
	                if (err){
	                    callback(err, null); 
	                }
	                else{
	                	console.log(result[0]);
	                	var amount = 0,count=0;
	                	result.forEach(function (item,index){
	                		amount += item.amount;
	                		count += item.count;
	                	});
	                	result.push({consumeSubject:'【合计】',count:count,amount:amount});
	                	callback(null, result); 
	                }
	        });
        }); 
    });
};

Consume.month = function month(username, callback)
{
    mongodb.open(function(err, db){
        if (err){
            return callback(err); 
        }
        // 读取 posts 集合
        db.collection('consume', function(err, collection){
            if (err){
                mongodb.close();
                return callback(err); 
            }
            
			var map = function(){
				emit(this.consumeDate.substr(0,7),{amount:this.consumeAmount,count:1});
			};
			
			var reduce = function(key,vals){
				var val = 0,count=0;
				for(var i=0; i<vals.length;i++){
					val += isNaN(vals[i].amount)?0:Number(vals[i].amount);
					count++;
				}
				return {amount:val,count:count};
			}
			
			console.log("统计:"+username);
            collection.mapReduce(
            	map,
        		reduce,
        		{out: {replace : 'temp', readPreference : 'secondary', query:{userName:username}}},
            	function(err, collection){
	                if (err){
                		mongodb.close(); 
	                    callback(err, null); 
	                }
	                else{
	                	//console.log(collection);
	                	/*
	                	var amount = 0,count=0;
	                	result.forEach(function (item,index){
	                		amount += item.amount;
	                		count += item.count;
	                	});
	                	result.push({consumeSubject:'【合计】',count:count,amount:amount});
	                	*/
	                	
	            		var results = [];
	                	collection.find().toArray(function(err,docs){
	                		docs.forEach(function(item,idx){
	                			console.log(item);
	                			//为了共用结果页面,统一使用consumeSubject
		                		results.push({consumeSubject:item._id,amount:item.value.amount,count:item.value.count});
	                		});
	                		mongodb.close();
		                	callback(null, results); 
	                	});
	                }
	        });
        }); 
    });
};


  全部源码见附件。

  另,2014-04-02
  系统开发好了之后,找了个服务器部署了一下,老婆用得不错,原来不太喜欢记账,现在记账很积极。我就又把系统完善了一下,增加了翻页、权限控制功能,将几个按钮修改成图标,布局更紧凑了一些,使用全局变量保存系统名称,方便修改。
  • 大小: 61.2 KB
  • 大小: 77.5 KB
  • 大小: 53.5 KB
  • 大小: 42 KB
  • 大小: 47 KB
  • account.rar (297 KB)
  • 描述: 个人记账系统源码
  • 下载次数: 1012
  • microblog.rar (250.8 KB)
  • 描述: 调试通过的书中例子
  • 下载次数: 529
分享到:
评论
1 楼 ldl_xz 2014-05-14  
这个是我当时学习的经历,可以借鉴一下http://www.9958.pw/post/nodejs_lesson

相关推荐

    新时期的node.js入门-李锴-书中示例代码

    总的来说,《新时期的Node.js入门》是一本全面介绍Node.js基础与实践的书籍,通过李锴精心编写的实例代码,读者不仅能学习到Node.js的基本语法,还能掌握实际开发中的各种技巧,为步入Node.js的世界打下坚实基础。

    Node.js入门经典 源代码

    《Node.js入门经典》的源代码提供了丰富的示例,涵盖上述知识点。通过分析和运行这些代码,你可以: 1. 学习如何设置项目结构和模块化代码。 2. 理解Node.js的错误处理机制。 3. 掌握Node.js中各种模块的使用方法,...

    node.js学习入门

    ### Node.js 学习入门知识点详解 #### 一、前言 Node.js 是一种流行的开源后端 JavaScript 运行环境,它使用 Google 的 V8 JavaScript 引擎来执行服务器端的 JavaScript。本书旨在帮助读者从零开始掌握 Node.js 的...

    node.js入门最佳示例demo

    最佳node入门例子,用到了express + ejs + mysql,程序功能是给出音乐列表,音乐可以播放,可以上传音乐。程序中没有用到其它各种包,很单纯,而且项目的readme文件中对...把本程序研究清楚,node.js就可以算是入门了。

    2013年Node.js入门经典源代码

    总之,这份2013年的"Node.js入门经典源代码"是一份宝贵的资源,它不仅让我们重温了Node.js的早期形态,也为我们提供了学习和理解JavaScript服务器端编程历史的窗口。通过深入研究,我们可以更好地掌握Node.js的核心...

    Node.js入门经典源代码

    这个"Node.js入门经典源代码"压缩包文件显然包含了用于学习和理解Node.js基础知识和实践的源代码示例。以下是一些关于Node.js的重要知识点,结合这个资源包,你可能会学到: 1. **异步非阻塞I/O**: Node.js的核心...

    Node.js入门源代码及开发工具

    **:这是每个编程语言的入门示例,Node.js也不例外。通过创建一个简单的JavaScript文件,输出"Hello, World!",你可以了解Node.js如何执行脚本。 2. **命令行工具**:学习如何创建命令行工具,例如读取用户输入、...

    nodejs 入门.pdf

    ### Node.js 入门知识点详解 #### 一、Node.js简介 Node.js 是一种让 JavaScript 运行在服务器端的应用平台。它利用 Chrome V8 引擎的强大性能,结合事件驱动和非阻塞 I/O 模型,为 Web 开发者提供了一种全新的构建...

    Node.js快速学习入门

    Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它让开发者能够在服务器端使用 JavaScript 进行开发,极大地推动了全栈开发的流行。本教程旨在为初学者提供一个快速学习 Node.js 的入口,帮助你从零开始...

    Node.js资料包,5本电子书籍

    通过深入阅读并实践这些书籍中的示例,你将能够建立坚实的Node.js基础,从而更好地利用JavaScript进行服务器端开发,实现全栈Web开发。无论你是初学者还是经验丰富的开发者,这些资源都将为你的Node.js技能树添加...

    Node.js.the.Right.Way(2013.11)

    这些内容有助于JavaScript开发者快速入门并深入理解Node.js在构建可扩展的服务器端应用中的使用。 书中被Allen Wirfs-Brock(ECMAScript语言规范的项目编辑)等人高度评价,Allen Wirfs-Brock甚至声称,对于刚开始...

    node.js开发指南 pdf+源码

    《Node.js开发指南》是一本面向初学者的Node.js教程,旨在帮助开发者快速掌握这个基于Chrome V8引擎的JavaScript运行环境。Node.js以其高效的非阻塞I/O模型和事件驱动特性,成为构建网络应用的强大工具,尤其在实时...

    node.js入门学习demo练习(基于菜鸟教程)

    在本资源中,我们关注的是"Node.js入门学习demo练习",这是一系列基于菜鸟教程的实践项目。Node.js是一个开放源代码、跨平台的JavaScript运行环境,它允许开发人员在服务器端执行JavaScript代码,极大地扩展了...

    Node.js-node.js中文资料导航

    学习 Node.js 不仅能让你构建高性能的 Web 应用,还能帮助你理解和应用现代 JavaScript 的特性,为全栈开发打下坚实基础。这个"Node.js中文资料导航"对于中文学习者来说,无疑是一个宝贵的资源集合。

Global site tag (gtag.js) - Google Analytics