深入理解Node系列-细说Connect(下)
Posted 你不知道的sharlly
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解Node系列-细说Connect(下)相关的知识,希望对你有一定的参考价值。
在细说 Connect (上)中,我们了解到了 Connect 的中间件架构,在架构中有许多透明的逻辑,比如事件的触发,body 的解析等等,而这些逻辑正是 node 中的基础模块以及 connect 的部分中间件完成的。
node 基础模块
1. events
我们都知道 Node 是事件驱动的服务器,而 events 就是其架构核心。很多基础模块都会继承 events 模块,比如 http 模块设置了 request 和 response 的 事件响应,fs 模块会在打开文件,读取文件,关闭文件时触发对应的事件,这一切都基于 events。 先来看一下 events 常用的 api:
1.1 on & emit
同浏览器端的事件,先绑定事件后触发。
const E = require('events');
const e = new E();
e.on('tap', function(data)
console.log(data.message);
);
e.emit('tap',
message: 'listen successful'
);
// 打印 listen successful
1.2 removeListener & removeAllListeners
同浏览器端的事件,可删除监听
// removeListener 不能删除绑定了匿名函数的事件监听
const callback = function() ;
e.on('tap', callback);
e.removeListener('tap', callback);
// removeAllListeners 会影响所有(不仅仅是你编写的模块,包括socket等的监听)
e.removeAllListeners('tap');
1.3 once & prependOnceListener
once 绑定的事件被触发一次后就被删除,prependOnceListener 绑定一个事件,并在其触发时先一步执行并删除。
e.once('tap', function() console.log('1') );
e.prependOnceListener('tap', function() console.log('2') );
e.emit('tap');
// 先打印 2 , 后打印 1
events 模块基于观察者模式
为我们提供了事件操作,以后会对这种设计模式进行详细讲解。
2. http
http 是 Node 中的内置模块,由 C++ 编写,性能十分优秀。这个模块提供了 http 客户端以及 http 服务端,使得 Node 能够以非常简单的方式进行服务器搭建,信息爬取,代理等等。
2.1 基于事件
之前提过 Node 中大多数模块是继承了 EventEmitter,http 也不例外,来看看在其中是怎么体现的。
const http = require('http');
http.createServer(function(req, res)
res.end('sharlly');
).listen(3000);
event 似乎很透明,那 createServer 做了什么事情呢?
// createServer
http.createServer = function(fn)
return new Server(fn);
// Server 构造
class Server extends EventEmitter
constructor(fn)
if (fn) this.on('request', fn);
此时就显然易见了。
2.2 http 服务端具体实现
http.Server
的回调函数前两个参数是 req 和 res,这两个分别是 http.IncomingMessage
和 http.ServerResponse
的实例化对象。
http.IncomingMessage
是 http 请求的信息,基本属性如下:
并且提供了3个事件:data , end,close。
var http = require('http');
var server = new http.Server();
server.on('request', function(req, res)
var data = '';
/*
请求体到来时触发 data 事件,提供一个参数 chunk,表示请求体数据
*/
req.on('data', function(chunk)
data += chunk;
);
/*
当请求体数据传输完毕时,该事件会被触发。
*/
req.on('end', function(chunl)
return data;
);
/*
服务器关闭触发
*/
/*
res 处理,接下
*/
);
http.ServerResponse
是返回客户端的信息,是触发 http.Server 的 request 事件后进行逻辑处理。
/*
res 处理,接上
*/
/*
向请求的客户端发送响应头,该函数在一个请求中最多调用一次,如果不调用,则会自动生成一个响应头。
*/
res.writeHead(status, [headers]);
/*
想请求的客户端发送相应内容,data是一个buffer或者字符串,如果data是字符串,则需要制定编码方式,默认为utf-8,在res.end调用之前可以多次调用
*/
res.write(data, [encoding]);
/*
结束响应,告知客户端所有发送已经结束,当所有要返回的内容发送完毕时,该函数必需被调用一次,两个可选参数与res.write()相同。如果不调用这个函数,客户端将用于处于等待状态。
*/
res.end([data],[encoding]);
同样的,res 也提供了三个事件 data,end,close。
connect常用中间件
1. body-parser
Node 或是使用 Connect 在处理请求数据时,是需要解析 req 的,body-parser 中间件恰好提供了这一操作。
它提供了 req.body 属性,用来解析 JSON, x-www-form-urlencoded。旧版本的 body-parser 还可以处理文件上传(现在需要使用 formidable 一类的文件上传模块)。
const connect = require('connect')();
const bodyParser = require('body-parser');
connect
.use(function(req, res, next)
console.log(req.body); // undefined
next();
)
.use(bodyParser())
.use(function(req, res)
console.log(req.body); // post 请求数据
)
.listen(3000);
不仅仅是 connect ,在 express 和 koa 中,同样也有 body-parser 中间件。
2. cookie-parser & jsonwebtoken
和 body-parser 类似,cookie-parser 将来自浏览器的 cookie 请求体转化到 req.cookies 上。它可以处理常规 cookie 和 签名 cookie。
在现在的前后端通信,特别是第三方认证盛行的模式下,cookie 和 sesssion 已不在是主流,再加之为了遵守 http 无状态的本质,往往会选择 token 方式进行认证。而 jsonwebtoken 恰好能让我们的 Node 程序实现这一认证。
const jwt = require('jsonwebtoken');
/**
* 对验证成功地请求体进行签名
* obj 被签名的主体
* signedText 密钥
* options 签名设置,如过期日期
*/
const token = jwt.sign(obj, signedText, options);
/**
* 进行验证
* client_token 客户端传来的 token 值
* signedText 密钥
*/
jwt.verify(client_token, signedText, function(err, decode)
if (err) return;
console.log(decode);
// 验证成功后 token 信息
);
常用模块
1. url & qs
url 顾名思义,就是为客户端请求地址的一个解析。而 qs 则是在 url 处理结果的基础上再进行字段解析。
const url = require('url');
const connect = require('connect');
const qs = require('qs');
connect()
.use(function(req, res)
const urlJson = url.parse(req.url);
console.log(urlJson);
/* 打印可得
Url
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?a=b',
query: 'a=b',
pathname: '/',
path: '/?a=b',
href: '/?a=b'
*/
const query = qs.parse(arg.query);
console.log(query);
/*
a: b
*/
)
.listen(3000);
2. formidable
要正确处理上传的文件,并接收到文件的内容,需要把表单的 enctype 属性设为 multipart/form-data。Node 社区有几个可以完成这个任务的模块,formidable 就是之一。简单使用如下:
const connect = require('connect')();
connect
.use(function(req, res)
if (req.method == 'POST')
const form = new formidable.IncomingForm();
// 上传路径
form.uploadDir = __dirname + '/public';
form.parse(req, function(err, fields, files)
res.end('upload complete!');
);
)
.listen(3000);
总结
connect 是一个非常轻量级 Node 中间件框架,对于一个中小型项目,connect 需要配合很多三方模块才能完善服务器逻辑。所以现在的工程中,为了简化开发,往往会采用它的升级版 express ,或是 koa 作为基础框架进行扩展。当然,在使用这些框架之前,对于最原始的细节,同样也是需要去仔细钻研的。
以上是关于深入理解Node系列-细说Connect(下)的主要内容,如果未能解决你的问题,请参考以下文章