深刻浅出Node.js(七):Connect模块解析(之一)

Connect模块背景

Node.js的愿望是成为一个能构建高速,可伸缩的网络应用的平台,它自己具备基于事件,异步,非阻塞,回调等特性,这在前几篇专栏中有过描述。正是基于这样的一些特性,Node.js平台上的Web框架也具备不一样于其余平台的一些特性,其中Connect是众多Web框架中的佼佼者。
Connect在它的官方介绍中,它是Node的一个中间件框架。超过18个捆绑的中间件和一些精选第三方中间件。尽管Connect可能不是性能最好的Node.jsWeb框架,但它却几乎是最为流行的Web框架。为什么Connect能在众多框架中胜出,其缘由不外乎有以下几个: 前端

  • 模型简单
  • 中间件易于组合和插拔
  • 中间件易于定制和优化
  • 丰富的中间件

Connect自身十分简单,其做用是基于Web服务器作中间件管理。至于如何如何处理网络请求,这些任务经过路由分派给管理的中间件们进行处理。它的处理模型仅仅只是一个中间队列,进行流式处理而已,流式处理可能性能不是最优,可是倒是最易于被理解和接受。基于中间件能够自由组合和插拔的状况,优化它十分容易。
Connect模块目前在NPM仓库的MDO(被依赖最多的模块)排行第八位。但这并无真实反映出它的价值,由于排行第五位的Express框架其实是依赖Connect建立而成的。关于Express的介绍,将会在后续的专栏中一一为你讲解。 node

中间件

让咱们回顾一下Node.js最简单的Web服务器是如何编写的: git

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, '127.0.0.1');

咱们从最朴素的Web服务器处理流程开始,能够看到HTTP模块基于事件处理网络访问无外乎两个主要的因素,请求和响应。同理的是Connect的中间件也是扮演这样一个角色,处理请求,而后响应客户端或是让下一个中间件继续处理。以下是一个中间件最朴素的原型: github

function (req, res, next) {
  // 中间件
}

在中间件的上下文中,有着三个变量。分别表明请求对象、响应对象、下一个中间件。若是当前中间件调用了res.end()结束了响应,执行下一个中间件就显得没有必要。 npm

流式处理

为了演示中间件的流式处理,咱们能够看看中间件的使用形式: json

var app = connect();
// Middleware
app.use(connect.staticCache());
app.use(connect.static(__dirname + '/public'));
app.use(connect.cookieParser());
app.use(connect.session());
app.use(connect.query());
app.use(connect.bodyParser());
app.use(connect.csrf());
app.use(function (req, res, next) {
  // 中间件
});
app.listen(3001);

Conncet提供use方法用于注册中间件到一个Connect对象的队列中,咱们称该队列叫作中间件队列。 后端

中间件队列

Conncet的部分核心代码以下,它经过use方法来维护一个中间件队列。而后在请求来临的时候,依次调用队列中的中间件,直到某个中间件再也不调用下一个中间件为止。 服务器

app.stack = [];
app.use = function(route, fn){
  // …

  // add the middleware
  debug('use %s %s', route || '/', fn.name || 'anonymous');
  this.stack.push({ route: route, handle: fn });

  return this;
};

值得注意的是,必需要有一个中间件调用res.end()方法来告知客户端请求已被处理完成,不然客户端将一直处于等待状态。
流式处理也是Node.js中用于流程控制的经典模式,Connect模块是典型的应用了它。流式处理的好处在于,每个中间层的职责都是单一的,开发者经过这个模式能够将复杂的业务逻辑进行分解。 cookie

路由

从前文能够看到其实app.use()方法接受两个参数,route和fn,既路由信息和中间件函数,一个完整的中间件,其实包含路由信息和中间件函数。路由信息的做用是过滤不匹配的URL。请求在碰见路由信息不匹配时,直接传递给下一个中间件处理。
一般在调用app.use()注册中间件时,只须要传递一个中间件函数便可。实际上这个过程当中,Connect会将/做为该中间件的默认路由,它表示全部的请求都会被该中间件处理。
中间件的优点相似于Java中的过滤器,可以全局性地处理一些事务,使得业务逻辑保持简单。
任何事物均有两面性,当你调用app.use()添加中间件的时候,须要考虑的是中间件队列是否太长,由于每一层中间件的调用都是会下降性能的。为了提升性能,在添加中间件的时候,如非全局需求的,尽可能附带上精确的路由信息。
以multipart中间件为例,它用于处理表单提交的文件信息,相对而言较为耗费资源。它存在潜在的问题,那就是有可能被人在客户端恶意提交文件,形成服务器资源的浪费。若是不采用路由信息加以限制,那么任何URL均可以被攻击。 网络

app.use("/upload", connect.multipart({ uploadDir: path }));

加上精确的路由信息后,能够将问题减少。

MVC目录

借助Connect能够自由定制中间件的优点,能够自行提高性能或是设计出适合本身须要的项目。Connect自身提供了路由功能,在此基础上,能够轻松搭建MVC模式的框架,以达到开发效率和执行效率的平衡。如下是笔者项目中采用的目录结构,清晰地划分目录结构能够帮助划分代码的职责,此处仅供参考。

├── Makefile // 构建文件,一般用于启动单元测试运行等操做
├── app.js // 应用文件
├── automation  // 自动化测试目录
├── bin  // 存放启动应用相关脚本的目录
├── conf  // 配置文件目录
├── controllers  // 控制层目录
├── helpers  // 帮助类库
├── middlewares  // 自定义中间件目录
├── models  // 数据层目录
├── node_modules  // 第三方模块目录
├── package.json  // 项目包描述文件
├── public  // 静态文件目录
│   ├── images  // 图片目录
│   ├── libs  // 第三方前端JavaScript库目录
│   ├── scripts  // 前端JavaScript脚本目录
│   └── styles  // 样式表目录
├── test  // 单元测试目录
└── views  // 视图层目录

参考:

关于做者

田永强,新浪微博@朴灵,前端工程师,曾就任于SAP,现就任于淘宝,花名朴灵,致力于NodeJS和Mobile Web App方面的研发工做。双修先后端JavaScript,寄望将NodeJS引荐给更多的工程师。兴趣:读万卷书,行万里路。我的Github地址:http://github.com/JacksonTian

相关文章
相关标签/搜索