深入理解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(下)的主要内容,如果未能解决你的问题,请参考以下文章

深入理解Node系列-细说Connect(上)

深入理解Node系列-细说Connect(上)

深入理解Node系列-细说Connect(上)

深入理解JavaScript系列(24):JavaScript与DOM(下)

[转]深入理解ajax系列——头部消息

深入理解ajax系列第三篇——头部信息