安装express
express 是 Node.js 上最流行的 Web 开发框架,正如他的名字一样,使用它我们可以快速的开发一个 Web 应用。我们用 express 来搭建我们的博客,打开命令行,输入:
npm install -g express-generator
用express创建项目
express -e login
- e指令使用ejs模板引擎,默认使用jade模板(jade好难用)
npm install
运行项目
npm start or node .bin/www
工程目录
app.js:启动文件,或者说入口文件
package.json:存储着工程的信息及模块依赖,当在 dependencies 中添加依赖的模块时,运行 npm install
,npm 会检查当前目录下的 package.json,并自动安装所有指定的模块
node_modules:存放 package.json 中安装的模块,当你在 package.json 添加依赖的模块并安装后,存放在这个文件夹下
public:存放 image、css、js 等文件
routes:存放路由文件
views:存放视图文件或者说模版文件
bin:存放可执行文件
app.js 代码如下
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var routes = 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', 'ejs');
// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', routes);
app.use('/users', users);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
不了解代码的去看express官网讲解很详细
看html比较习惯,可以将ejs后缀改为html,并修改app.js加载ejs模板代码如下:
app.set('views', path.join(__dirname, 'views'));
app.engine('html', require('ejs').__express);
app.set('view engine', 'html');
再来安装该项目所需模块
登录需要用到session模块,session有依赖于cookie,所以要安装session和cookie模块,最重要的是socket
npm install cookie-parser
npm install express-session
首先是主界面 index.html
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
<p><a href="/login">登录</a></p>
<p><a href="/register">注册</a></p>
</body>
</html>
登录页面 login.html
<div class="container">
<%- message %> //用户操作信息
<form class="form-horizontal col-sm-4 col-sm-offset-4" role="form" method="post" onsubmit="return false">
<fieldset>
<legend></legend>
<div class="panel panel-default">
<div class="page-header">
<p class="tc">请先登录</p>
</div>
<div class="panel-body m15">
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">
<span class="glyphicon glyphicon-user"></span>
</span>
<input type="text" class="form-control" id="username" name="username" placeholder="请输入用户名" required>
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">
<span class="glyphicon glyphicon-lock"></span>
</span>
<input type="password" class="form-control" id="password" name="password" placeholder="请输入密码" required>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn bg-primary btn-block" id="register0">注册</button>
</div>
<div class="form-group">
<button type="button" class="btn btn-info btn-block" id="login0">登录</button>
</div>
</div>
</div>
</fieldset>
</form>
</div>
注册页面 register.html(和login页面同理)
<div class="container">
<%- message %>
<form class="form-horizontal col-sm-4 col-sm-offset-4" role="form" method="post" onsubmit="return false">
<fieldset>
<legend></legend>
<div class="panel panel-default">
<div class="page-header">
<p class="tc">注册信息</p>
</div>
<div class="panel-body m15">
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">
<span class="glyphicon glyphicon-user"></span>
</span>
<input type="text" class="form-control" id="username" name="username" placeholder="请输入用户名" required>
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">
<span class="glyphicon glyphicon-lock"></span>
</span>
<input type="password" class="form-control" id="password" name="password" placeholder="请输入密码" required>
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">
<span class="glyphicon glyphicon-lock"></span>
</span>
<input type="text" class="form-control" id="passwordAgin" name="passwordAgin" placeholder="请再次输入密码">
</div>
</div>
<div class="form-group">
<button type="submit" class="btn bg-primary btn-block" id="register1">注册</button>
</div>
</div>
</div>
</fieldset>
</form>
</div>
//提交用户注册信息
$(function() {
$("#register1").click(function() {
var username = $("#username").val();
var password = $("#password").val();
var passwordAgin = $("#passwordAgin").val();
if (password !== passwordAgin) {
$("#password").css("boder", "1px solid red");
$("#passwordAgin").css("boder", "1px solid red");
} else if (password === passwordAgin) {
var data = {
"uname": username,
"upwd": password
};
$.ajax({
url: '/register',
type: 'post',
data: data,
success: function(data, status) {
if (status == 'success') {
location.href = 'login';
}
},
error: function(data, err) {
location.href = 'register';
}
});
}
});
});
登录后进入home.html
<h1>your name:<%- user.name%></h1>
<p>Welcom to your home</p>
<p><a href="/chat">进入聊天室</a></p>
<p><a href="/logout">我要注销</a></p>
聊天页面 chat.html
<div id="chatbox">
<div style="background:#3d3d3d;height: 28px; width: 100%;font-size:12px;">
<div style="line-height: 28px;color:#fff;">
<span style="text-align:left;margin-left:10px;">Websocket多人聊天室</span>
<span style="float:right; margin-right:10px;"><span id="showusername"></span> |
<a href="javascript:;" onclick="CHAT.logout()" style="color:#fff;">退出</a></span>
</div>
</div>
<div id="doc">
<div id="chat">
<div id="message" class="message">
<div id="onlinecount" style="background:#EFEFF4; font-size:12px; margin-top:10px; margin-left:10px; color:#666;">
</div>
</div>
<div class="input-box">
<div class="input">
<input type="text" maxlength="140" placeholder="请输入聊天内容,按Enter提交" id="content" name="content">
</div>
<div class="action">
<button type="button" id="mjr_send" onclick="CHAT.submit();">提交</button>
</div>
</div>
</div>
</div>
</div>
使用express中间件,对每个请求进行处理,获取session(获取不到session对象则没有正常登录,或登录失效,并返回错误信息),代码如下:
app.use(cookieParser());
app.use(session({
secret: 'secret',
cookie: {
maxAge: 1000 * 60 * 30
}
}));
app.use(function(req, res, next) {
res.locals.user = req.session.user; //从session获取user对象
var err = req.session.error; //获取错误信息
delete req.session.error;
res.locals.message = ""; //展示信息的message
if (err) {
res.locals.message = '<div class="alert alert-danger" style="margin-bottom:20px;color:red;text-align:center;">' + err + '</div>';
}
next(); //中间件传递
});
本项目用的是mongodb数据库,首先安装mongoose模块
npm install -g mongoose
npm install -g mongoose
引入连接并创建数据模型,db.js如下:
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://127.0.0.1:27017');//mongodb默认端口27017.
var Schema = mongoose.Schema; // 创建模型
var userScheMa = new Schema({
name: String,
password: String
}); // 定义了一个新的模型,但是此模式还未和users集合有关联
exports.Model = db.model('users', userScheMa); // 与users集合关联
服务端登录检查,login.js如下:
var users = require('../database/db').Model;//引入usersji'h
function login(req,res){
var username = req.body.username,
password = req.body.password;
users.find({name:username},function(err,result){
if(!result[0]){
res.end(JSON.stringify({'code':500,'data':'用户名不存在'}));
}
else{
if(result[0].password==password){
res.end(JSON.stringify({'code':200,'data':{name:result[0].name,maxproject:result[0].maxProjects}}));
}
else{
res.end(JSON.stringify({'code':500,'data':'密码不正确'}));
}
}
})
}
module.exports = login;
服务端用户注册功能,register.js如下:
var users = require('../database/db').Model;
function register(req,res){
var username = req.body.username,
password = req.body.password;
users.find({name:username},function(err,result){
if(result[0]){
res.end(JSON.stringify({'code':500,'data':'用户名已存在'}))
}
else{
users.create({name:username,password:password,maxProjects:6,projects:[],recycle:[]},function(err){
if(!err){
res.end(JSON.stringify({'code':200,'data':'注册成功'}));
}
});
}
})
}
module.exports = register;
客户端用户登录,代码如下:
$(function() {
$("#register0").click(function() {
location.href = 'register';
});
$("#login0").click(function() {
var username = $("#username").val();
var password = $("#password").val();
var data = {
"uname": username,
"upwd": password
};
$.ajax({
url: '/login',
type: 'post',
data: data,
success: function(data, status) {
location.href = 'home';
localStorage.setItem('name', username);
},
error: function(data, status) {
location.href = 'login';
}
});
});
});
客户端用户注册,代码如下:
$(function() {
$("#register1").click(function() {
var username = $("#username").val();
var password = $("#password").val();
var passwordAgin = $("#passwordAgin").val();
if (password !== passwordAgin) {
$("#password").css("boder", "1px solid red");
$("#passwordAgin").css("boder", "1px solid red");
} else if (password === passwordAgin) {
var data = {
"uname": username,
"upwd": password
};
$.ajax({
url: '/register',
type: 'post',
data: data,
success: function(data, status) {
if (status == 'success') {
location.href = 'login';
}
},
error: function(data, err) {
location.href = 'register';
}
});
}
});
});
接下来是聊天功能,服务端需要安装socket.io模块,该模块可以对socket套接字操作,代码如下:
function chatready(io) {
//在线用户
var onlineUsers = {};
//当前在线人数
var onlineCount = 0;
io.on('connection', function(socket) {
console.log('a user connected');
//监听新用户加入
socket.on('login', function(obj) {
//将新加入用户的唯一标识当作socket的名称,后面退出的时候会用到
socket.name = obj.username;
//检查在线列表,如果不在里面就加入
if (!onlineUsers.hasOwnProperty(obj.username)) {
onlineUsers[obj.username] = obj.username;
//在线人数+1
onlineCount++;
}
//向所有客户端广播用户加入
io.emit('login', { onlineUsers: onlineUsers, onlineCount: onlineCount, user: obj });
console.log(obj.username + '加入了聊天室');
});
//监听用户退出
socket.on('disconnect', function() {
//将退出的用户从在线列表中删除
if (onlineUsers.hasOwnProperty(socket.name)) {
//退出用户的信息
var obj = { username: onlineUsers[socket.name] };
//删除
delete onlineUsers[socket.name];
//在线人数-1
onlineCount--;
//向所有客户端广播用户退出
io.emit('logout', { onlineUsers: onlineUsers, onlineCount: onlineCount, user: obj });
console.log(obj.username + '退出了聊天室');
}
});
//监听用户发布聊天内容
socket.on('message', function(obj) {
//向所有客户端广播发布的消息
io.emit('message', obj);
console.log(obj.username + '说:' + obj.content);
});
});
}
module.exports = chatready;
找到bin文件夹下的www文件,引入chatready模块,并绑定socket到http服务器上var server = http.createServer(app);
var io = require('socket.io')(server);
chartready(io);
客户端需要引入socket.io 这个js文件,可从node_modules模块文件夹下找到socket.io中找到改文件,客户端代码如下:
(function() {
var d = document,
w = window,
p = parseInt,
dd = d.documentElement,
db = d.body,
dc = d.compatMode == 'CSS1Compat',
dx = dc ? dd : db,
ec = encodeURIComponent;
w.CHAT = {
msgObj: d.getElementById("message"),
screenheight: w.innerHeight ? w.innerHeight : dx.clientHeight,
username: null,
socket: null,
//让浏览器滚动条保持在最低部
scrollToBottom: function() {
w.scrollTo(0, this.msgObj.clientHeight);
},
//退出,本例只是一个简单的刷新
logout: function() {
//this.socket.disconnect();
location.href = 'login';
},
//提交聊天消息内容
submit: function() {
var content = d.getElementById("content").value;
if (content != '') {
var obj = {
userid: this.userid,
username: this.username,
content: content
};
this.socket.emit('message', obj);
d.getElementById("content").value = '';
}
return false;
},
genUid: function() {
return new Date().getTime() + "" + Math.floor(Math.random() * 899 + 100);
},
//更新系统消息,本例中在用户加入、退出的时候调用
updateSysMsg: function(o, action) {
//当前在线用户列表
var onlineUsers = o.onlineUsers;
//当前在线人数
var onlineCount = o.onlineCount;
//新加入用户的信息
var user = o.user;
//更新在线人数
var userhtml = '';
var separator = '';
for (key in onlineUsers) {
if (onlineUsers.hasOwnProperty(key)) {
userhtml += separator + onlineUsers[key];
separator = '、';
}
}
d.getElementById("onlinecount").innerHTML = '当前共有 ' + onlineCount + ' 人在线,在线列表:' + userhtml;
//添加系统消息
var html = '';
html += '<div class="msg-system">';
html += user.username;
html += (action == 'login') ? ' 加入了聊天室' : ' 退出了聊天室';
html += '</div>';
var section = d.createElement('section');
section.className = 'system J-mjrlinkWrap J-cutMsg';
section.innerHTML = html;
this.msgObj.appendChild(section);
this.scrollToBottom();
},
init: function(username) {
/*
客户端根据时间和随机数生成uid,这样使得聊天室用户名称可以重复。
实际项目中,如果是需要用户登录,那么直接采用用户的uid来做标识就可以
*/
this.username = username;
d.getElementById("showusername").innerHTML = this.username;
//this.msgObj.style.minHeight = (this.screenheight - db.clientHeight + this.msgObj.clientHeight) + "px";
this.scrollToBottom();
//连接websocket后端服务器
this.socket = io.connect('ws://127.0.0.1:3000');
//告诉服务器端有用户登录
this.socket.emit('login', { username: this.username });
//监听新用户登录
this.socket.on('login', function(o) {
CHAT.updateSysMsg(o, 'login');
});
//监听用户退出
this.socket.on('logout', function(o) {
CHAT.updateSysMsg(o, 'logout');
});
//监听消息发送
this.socket.on('message', function(obj) {
var isme = (obj.username == CHAT.username) ? true : false;
var contentDiv = '<div>' + obj.content + '</div>';
var usernameDiv = '<span>' + obj.username + '</span>';
var section = d.createElement('section');
if (isme) {
section.className = 'user';
section.innerHTML = contentDiv + usernameDiv;
} else {
section.className = 'service';
section.innerHTML = usernameDiv + contentDiv;
}
CHAT.msgObj.appendChild(section);
CHAT.scrollToBottom();
});
}
};
//通过“回车”提交信息
d.getElementById("content").onkeydown = function(e) {
e = e || event;
if (e.keyCode === 13) {
CHAT.submit();
}
};
CHAT.init(localStorage.getItem('name'));
})();