想必对于广大前后端的同学们,Node 或是用来作为网站服务器的搭建,亦或是用来作为开发脚手架的运用,或是早有套路,亦或是浅尝辄止。从现在开始博主将会不定时的对 Node 系列的产品做分析,其中夹杂着常见的基础模块,三方模块,丰富大家的 Node 技术栈。
很多童鞋上手项目时,通常会将 作为 Node 端框架,而本文主要对其底层构件 做一个分析。
Connect 是一个可扩展 (中间件作为插件) 的 Http 服务器框架,Connect 刚出道之时自带了许多中间件,为保证其框架的轻量级以及扩展性,最终还是将这些中间件的实现抛给了社区。可能在搜索 Connect 的相关项目时,你会发现
这些的写法,这对于现在的 Connect (最新版本 3.6.0) 是不支持的,而只能通过 npm 下载第三方的模块 (如 body-parser) 替代原先的中间价。
- connect().use(connect.bodyParser())
1. 基本使用
Connect 提供的 API 不多,并且非常容易理解。
listen
Connect 引入了
原生模块,因此 listen 也是用来监听端口的。
- http
- var connect = require('connect');
- var PORT = 3000;
- connect()
- .use(function(req, res) {
- res.end('listen port is ' + PORT);
- })
- .listen(PORT);
- // 访问localhost:3000
use
在 req/res 中有许多内容需要通过中间件处理才能方便取到,而 use 正好为中间件提供了一个入口。
- var connect = require('connect');
- var cookieParser = require('cookie-parser');
- connect()
- .use(function(req, res, next) {
- console.log('未使用cookie-parser', req.cookies);
- next();
- })
- .use(cookieParser())
- .use(function(req, res) {
- console.log('使用cookie-parser', req.cookies);
- res.end('.');
- })
- .listen(3000);
执行
会得到
- curl http://localhost:3000 -H "Cookie: name=sharlly"
- 未使用cookie-parser undefined
- 使用cookie-parser { name: 'sharlly' }
你可能会关注到 cookieParser 上面的函数比下面的多了一个
参数,这是 Connect 对中间件设定,只用调用了 next() 才会继续下一个中间件的执行,最后一个中间件则不需要使用。
- next
挂载 url
如果你想针对某个访问路径做出不同的响应 (即挂载,如设置用户访问权限),则同样可以使用 use() ,不过写法有所改变,如我希望访问 /home/… 和 /articles/… 并得到不同的内容。
- var connect = require('connect');
- connect()
- .use('/home', function(req, res) {
- res.end('home');
- })
- .use('/articles', function(req, res) {
- res.end('articles');
- })
- .use(function(req, res) {
- res.end('others');
- })
- .listen(3000)
2. 源码剖析
connect 的源码非常简短,可简单整理如下图:
use 函数
- proto.use = function use(route, fn) {
- var handle = fn;
- var path = route;
- // default route to '/'
- if (typeof route !== 'string') {
- handle = route;
- path = '/';
- }
- // wrap sub-apps
- if (typeof handle.handle === 'function') {
- var server = handle;
- server.route = path;
- handle = function(req, res, next) {
- server.handle(req, res, next);
- };
- }
- // wrap vanilla http.Servers
- if (handle instanceof http.Server) {
- handle = handle.listeners('request')[0];
- }
- // strip trailing slash
- if (path[path.length - 1] === '/') {
- path = path.slice(0, -1);
- }
- // add the middleware
- debug('use %s %s', path || '/', handle.name || 'anonymous');
- this.stack.push({
- route: path,
- handle: handle
- });
- return this;
- };
非常好理解,use() 将 route 和 function 一一对应并保存到
当中,若只有一个参数,则默认 route = '/' 。那保存下来的函数在哪里执行呢?再来看看
- this.stack
函数。
- handle
handle 函数
- proto.handle = function handle(req, res, out) {
- var index = 0;
- var protohost = getProtohost(req.url) || '';
- var removed = '';
- var slashAdded = false;
- var stack = this.stack;
- // final function handler
- var done = out || finalhandler(req, res, {
- env: env,
- onerror: logerror
- });
- // store the original URL
- req.originalUrl = req.originalUrl || req.url;
- function next(err) {
- // omit...
- var layer = stack[index++];
- // call the layer handle
- call(layer.handle, route, err, req, res, next);
- }
- next();
- };
当有请求进入到 Node 后,http 模块会触发
事件,此时会执行一次
- request
。而在 handle 函数中可以看到回调方法
- handle(req, res, next)
,通过
- next()
将下一个 layer.handle 传递给
- stack[index++]
执行,call() 带了几个参数,分别是
- call()
,
- use中自定义的方法
,
- 请求路径
,
- 错误对象
,
- 请求对象
,
- 响应对象
。
- handle()中的next()
而 call 方法也是非常简单
- function call(handle, route, err, req, res, next) {
- var arity = handle.length;
- var error = err;
- var hasError = Boolean(err);
- debug('%s %s : %s', handle.name || '', route, req.originalUrl);
- try {
- if (hasError && arity === 4) {
- // error-handling middleware
- handle(err, req, res, next);
- return;
- } else if (!hasError && arity < 4) {
- // request-handling middleware
- handle(req, res, next);
- return;
- }
- } catch (e) {
- // replace the error
- error = e;
- }
- // continue
- next(error);
- }
call 将 next 传给 use 自定义的函数上,这样
就可以调用下一个中间件了,直到最后一个中间件执行完后,之前的中间件 next 后面的代码才会按作用域依次执行。
- 在自定义函数中调用 next
整个过程总结如下:
通过分析 Connect 源码,对其中间件运行机制也有了一定的掌握。下篇,将会对 Connect 常用中间件以及第三方模块进行介绍。
来源: http://www.cnblogs.com/yxy99/p/6502073.html