Starter
Allows you to build scalable network applications using JavaScript on the server-side.
Node.js:
- 用JavaScript编写服务端程序
V8
引擎- 采用
单线程
+非阻塞IO
的方式(以事件
的方式通知回调函数
)达到并行的目的(高吞吐量,充分利用CPU资源) - 适合场景:
- 数据密集型(大量读取数据文件)
- 实时交互系统
- 不适合场景:
- 计算量大
- 逻辑太复杂
nodeJS 本身并没有根目录的概念,并不能读取某个文件夹的静态文件,只能一个文件一个文件的运行 nodeJS 自身封装了一些模块,使用require(moduleName)引用 nodeJS 本身不具备路由功能,服务跑起来后,不管地址栏添加任何path,结果都不变
npm
npm (Node Package Manager)
- node包管理和分发工具,node自动安装
- 能很快找到特定服务需要的包,下载安装管理
# -g 表示全局,不加-g,则安装在当前目录的node_modules下
> npm install -g xxx
# 下载包并记录在package.json文件中的devDependencies下
> npm install xxx --save-dev
# 下载包并记录在package.json文件中的dependencies下
> npm install xxx --save
# 下载指定version
> npm install xxx@version
# 卸载node模块
> npm uninstall moudleName
# 更新node模块
> npm update moduleName
# 根据当前目录下的package.json文件中声明的内容安装(dependencies的内容)
> npm install
# 初始化(会引导你创建一个package.json文件)
> npm init
# 查看当前目录下已安装的node包
> npm list
# 查看当前包的安装路径
> npm root
# 查看全局的包的安装路径
> npm root -g
# 查看npm安装的版本
> npm -v
HelloWorld
nodeTest/01.js:
var http=require('http');
http.createServer(function(request,response){
console.log(request.url);
res.writeHead(200,{'Content-Type':'text/plain'});
res.end('Hello world');
}).listen(1337);
console.log('Listening on port 1337' );
执行:
> node nodeTest/01.js
浏览器中访问:http://localhost:1337
,http://localhost:1337/123
查看效果
Events
var EventEmitter=require('events').EventEmitter;
var logger=new EventEmitter();
//绑定事件
logger.on('error',function(msg){
console.log('Err:'+msg);
});
//触发事件
logger.emit('error','Wrong number');
logger.emit('error','Unknow cmd');
Node里面的许多对象都会emit事件
例如:
class http.Server
是一个EventEmitter
,当有一个request
时,就会通知绑定的function(request,response){...}
执行
http.createServer(function(request,response){...});
//等价于:
var server=http.createServer();
server.on('request',function(request,response){...});
Streams
Stream可分为:
- readable
- 事件
- readable
- data
- end
- close
- error
- 方法
- read
- pause
- resume
- pipe
- unpipe
- 事件
- writeable
- 事件
- drain
- finish
- pipe
- unpipe
- 方法
- write
- end
- 事件
- readable & writeable
例如1:
http.createServer(function(request,response){
response.writeHead(200);
request.on('data',function(chunk){
response.write(chunk);
});
request.on('end',function(){
response.end();
})
}).listen(8080)
等同于:
http.createServer(function(request,response){
response.writeHead(200);
request.pipe(response)
}).listen(8080)
request
为readable streamresponse
为writable stream- 测试:
> curl -d 'hello' http://localhost:8080` > curl --upload-file readme.md http://localhost:8080`
例如2:
readStream.on('data',function(chunk){
var buffer_good =writeStream.write(chunk); //返回true/false,表明数据是否被写入目标了
if (!buffer_good) //为false,表示数据临时放在了缓存,而未写入目标
readStream.pause(); //暂停读取
});
writeStream.on('drain',function(){
readStream.resume(); //恢复读取
})
等同于:
//pipe可以解决当延迟非常大时导致的读写不平衡问题
readStream.pipe(writeStream);
Modules
- 定义一个Module:
module.exports=xxx
- 调用自定义Module:
require(path)
使用相对路径或绝对路径 - 调用外部的Module:
require(moduleName)
会从当前目录逐层往上搜索node_modules
目录
示例:
// makeRequest.js
var http=require('http');
module.exports=function(opts,message){
var request=http.request(opts,function(response){
response.on('data',function(data){
console.log(data);
});
request.write(message);
request.end();
})
}
调用
var makeRequest=require('./makeRequest');
markeRequest({host:'localhost','port':8080,path:'/',method:'POST'},"Hello world!");
Express
Express.js框架: 极大的简化了nodeJS开发
> npm install express
var express=require('express');
var app=express();
...
app.listen(1337,function(){
console.log("Listen on port 1337");
});
Middleware
一个Express应用,从本质上来说,就是一系列中间件的调用
中间件就是一函数,形式如下:
function(request,response,next){ ... //next(); or next(xxx); or response.xxx }
//错误处理的中间件 function(err, req, res, next){ ... //err.status,err.message }
- request
- query string:
request.query.xxx
- placeholder in url:
request.params.xxx
- query string:
- response
- response.send(xxx),response.json(xxx),response.sendFile(xxx)
- response.status(xxx), response.status(xxx).json(xxx)
- response.render(xxx)
- response.redirect(xxx)
- next
- next()表示传递给下一个中间件进行处理
- 如果带参数,则代表抛出一个错误
- request
注册中间件
app.use([path], [function, ...] function)
app.METHOD([path], [function, ...] function)
(METHOD可为get,post,put,delete,all,param等)
使用举例1:Express的static middleware
- 项目目录结构:
nodeTest - app.js - public/ - blocks.jpg - index.html
- 使用static middleware
app.use(express.static('public'));
- 访问静态资源:
- GET
http://localhost:1337/blocks.jpg
- GET
http://localhost:1337/index.html
- GET
使用举例2:自定义Middleware
- logger.js
module.exports=function(request,response,next){ ... next(); }
- 使用自定义的logger middleware
var logger=require('./logger'); app.use(logger);
Route Instances
app.route('/blocks')
.get(function(request,response,next){
...
})
.post(parseUrlencoded, function(request, response) {
...
});
app.route('/blocks/:name')
.all(function(request,response,next){
...
next();
})
.get(function(request,response,next){
...
})
.delete(function(request,response,next){
...
});
从Express 4.0开始,路由器功能成了一个单独的组件Express.Router
- router.use(...)
- router.METHOD(...)
- router.route(...)
...
为[path], [function, ...] function
var express = require('express');
var router=express.Router();
router.route('/')
.get(function(request,response,next){
...
})
.post(parseUrlencoded, function(request, response) {
...
});
router.route('/:name')
.all(function(request,response,next){
...
next();
})
.get(function(request,response,next){
...
})
.delete(function(request,response,next){
...
});
module.exports = router;
var blocks=require('./routes/blocks');
app.use('/blocks',blocks);
Request Params
示例1:request.params.xxx
,request.query.xxx
app.get('/blocks/:name', function(request, response) {
console.log(request.params.name);
console.log(request.query.limit);
response.send(request.query.limit+","+request.params.name);
});
> curl -i http://localhost:1337/blocks/test?limit=5
HTTP/1.1 200 OK
"5,test"
示例2:app.param(...)
app.param('name', function(request, response, next) {
request.name=request.params.name;
next();
});
app.get('/blocks/:name', function(request, response,next) {
console.log(request.name);
console.log(request.params.name);
...
});
示例3:app.all(...)
app.all('/blocks/:name',function(request,response,next){
request.name=request.params.name;
next();
})
app.get('/blocks/:name', function(request, response,next) {
console.log(request.name);
console.log(request.params.name);
...
});
常用module
body parser
Node.js body parsing middleware.provides the following parsers: (官网)
- JSON body parser
bodyParser.json(options)
(parseapplication/json
) - Raw body parser
bodyParser.raw(options)
- Text body parser
bodyParser.text(options)
- URL-encoded form body parser
bodyParser.urlencoded(options)
(parseapplication/x-www-form-urlencoded
)
注意:body-parser does not handle multipart bodies (无法解析multipart/form-data
)
使用示例:
安装body-parser
包(parse form elements to object properties)
> npm install body-parser
后台使用:( request.body
:get form data)
var bodyParser=require('body-parser');
var parseUrlencoded=bodyParser.urlencoded({extended:false});
app.post('/blocks',parseUrlencoded,function(request,response,next){
//request.body.xxx
...
})
前台触发:
$.ajax({
type:'POST',url:'/blocks',data:$("#blockForm").serialize()
}).done(function(data){
console.log(data);
});
formidable
> npm install formidable
后端:
var formidable=require('formidable');
app.post('/blocks',function(req,res){
var form=formidable.IncomingForm();
form.on('field',function(name,value){
console.log(name+":"+value);
});
form.on('file',function(name,file){
console.log(name+"+"+file.size);
});
form.on('end',function(){
console.log('end');
...
});
});
var formidable=require('formidable');
app.post('/blocks',function(req,res){
var form=formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
console.log(fields);
console.log(files);
});
});
var formidable=require('formidable');
app.post('/blocks',function(req,res){
var form=formidable.IncomingForm();
//all fields and files are collected and passed to the callback
form.parse(req, function(err, fields, files) {
console.log(arguments);
//end
res.json({success:false,data:JSON.stringify(block)});
});
//overwrite onPart to filter
form.onPart = function(part) {
if(part.filename){
//在这里处理file,不再交给formidable继续处理(不会再触发file事件)
part.pipe(fs.createWriteStream(part.filename));
}else{
form.handlePart(part); //交给formidable继续处理
}
};
// 处理POST 普通数据( 不包含文件 )
form.on('field',function(name,value){
block[name]=value;
});
});
前端:
var formData = new FormData($("form")[0]);
$.ajax({
url: '/blocks',
type: 'POST',
data: formData,
processData: false
}).done(function(data){
console.log(data);
});
session
> npm install cookie-parser
> npm install express-session
示例1:
var cookieParser = require('cookie-parser');
var session=require("express-session");
app.use(cookieParser)
.use(session({
resave:false,
saveUninitialized:true,
secret:"xxxx"
}));
app.post("/j_spring_security_check",function(request,response,next){
request.session.loginUser=request.body.username;
...
});
app.get('/j_spring_security_logout',function(request,response){
console.log(request.session);
request.session.destroy(function(err){
if(err)
throw err;
console.log("destroy session!");
});
...
});
示例2:使用Redis存储session
> npm install connect-redis
var RedisStore = require('connect-redis')(session);
app.use(session({
store: new RedisStore({
host: "127.0.0.1",
port: 6379,
db: "test_session"
}),
resave:false,
saveUninitialized:false,
secret: 'xxx'
}));
示例3:使用MongoDB存储session
> npm install connect-mongo
var MongoStore = require('connect-mongo')(session);
app.use(session({
store: new MongoStore({
host: 'localhost',
port: 27017,
db: 'test-app'
})
secret: 'xxx',
resave: false,
saveUninitialized: true,
}));
view engine
示例:使用ejs
> npm install ejs
var ejs=require("ejs");
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');
app.engine('html',ejs.__express) ;
app.get('/test',function(request,response,next){
request.render('test', {title: 'Test Message'});
});
Socket.io
Using duplexed websocket connection
安装
> npm install socket.io
使用示例:
app.js
var socket=require('socket.io');
var express=require('express');
var path = require('path');
var app=express();
var server = require('http').Server(app);
var io=socket.listen(server);
server.listen(1337);
app.get('/', function(req, res){
//res.send('<h1>Hello world</h1>');
res.sendFile(path.join(__dirname,'/01.html'));
});
io.set('log level', 2); // 0:error,1:warn,2:info,3:debug
io.sockets.on('connection',function(client){
client.on('join',function(name){
client.set('nickname',name);
client.broadcast.emit("status",name+" joined!");
});
client.on('message',function(data){
client.get('nickname',function(err,name){
io.sockets.emit("chat",name+" : "+data);
});
});
client.on('disconnect',function(name){
client.get('nickname',function(err,name){
client.broadcast.emit("status",name+" leaved!");
});
});
});
01.html
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript" src="vender/jquery/jquery.js"></script>
<script type="text/javascript">
var server=io.connect('http://localhost:8080');
var content=$("#content");
server.on("connect",function(){
content.html('Connected');
var nickname=prompt("What is your nickname?");
server.emit("join",nickname);
$("#clientName").text("--"+nickname);
});
server.on("chat",function(data){
content.append("<p>"+data+"</p>");
});
server.on("status",function(data){
content.append("<div><small>"+data+"</small></div>");
});
$("#chatForm").on("submit",function(event){
event.preventDefault();
server.emit("message",$("#chatInput").val());
$("#chatInput").val("");
});
</script>
测试
> node nodeTest/app.js