深入理解Node系列-细说Connect(上)
来源:互联网 发布:淘宝刷单处罚新规则 编辑:程序博客网 时间:2024/05/29 17:42
前言
想必对于广大前后端的同学们,Node 或是用来作为网站服务器的搭建,亦或是用来作为开发脚手架的运用,或是早有套路,亦或是浅尝辄止。从现在开始博主将会不定时的对 Node 系列的产品做分析,其中夹杂着常见的基础模块,三方模块,丰富大家的 Node 技术栈。
很多童鞋上手项目时,通常会将 Express 作为 Node 端框架,而本文主要对其底层构件 Connect 做一个分析。
connect
Connect 是一个可扩展(中间件作为插件)的 Http 服务器框架,Connect 刚出道之时自带了许多中间件,为保证其框架的轻量级以及扩展性,最终还是将这些中间件的实现抛给了社区。可能在搜索 Connect 的相关项目时,你会发现 connect().use(connect.bodyParser())
这些的写法,这对于现在的 Connect (最新版本3.6.0) 是不支持的,而只能通过 npm 下载第三方的模块 (如 body-parser) 替代原先的中间价。
1. 基本使用
Connect 提供的 API 不多,并且非常容易理解。
listen
Connect 引入了 http
原生模块,因此 listen 也是用来监听端口的。
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 上面的函数比下面的多了一个 next
参数,这是 Connect 对中间件设定,只用调用了 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 一一对应并保存到 this.stack
当中,若只有一个参数,则默认 route = ‘/’ 。那保存下来的函数在哪里执行呢?再来看看 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(req, res, next)
。而在 handle 函数中可以看到回调方法 next()
,通过 stack[index++]
将下一个 layer.handle 传递给 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 || '<anonymous>', 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 常用中间件以及第三方模块进行介绍。
- 深入理解Node系列-细说Connect(上)
- 深入理解Node系列-细说Connect(下)
- node深入理解系列
- 深入理解connect by
- 【网络基本功系列四】细说交换机细说路由(上)
- 理解Node.js中间件以及Connect 模块
- 理解Node.js中间件以及Connect 模块
- 网络基本功系列:细说路由(上)
- 深入理解node中的http
- 深入理解 Stream (Node.js)
- 深入理解JavaScript系列
- 深入理解JavaScript系列
- 深入理解JavaScript系列
- 深入理解android 系列
- 深入理解JavaScript系列
- 深入理解JavaScript系列
- 深入理解JavaScript系列
- 深入理解JavaScript系列
- 小程序开发https官方推荐解决方案
- 回文素数
- oracle cursor and exception
- Java中的基础----堆与栈的介绍、区别
- Json与javaBean之间的转换工具类
- 深入理解Node系列-细说Connect(上)
- Java.utils.concurrent包中的几个有用的类(CountDownLatch、CyclicBarrier、Semaphore)
- Netty学习之旅------源码分析Netty解码编码器实现原理
- 迭代工具(二)—— 批量对多个要素类进行坐标转换
- spring中 shiro logout 配置方式
- 【JAVA WEB】TOMCAT服务器配置HTTPS
- 从此不再惧怕URI编码:JavaScript及C# URI编码详解
- java之成绩总结
- Vulkan编程指南翻译 第六章 着色器和管线 第2节 SPIR-V 概述