深入浅出Node.js---Connect模块解析 。转载
文章地址:https://blog.csdn.net/zhangyuan19880606/article/details/51509205
1 Connect模块背景
Node.js的愿望是成为一个能构建高速,可伸缩的网络应用的平台,它本身具有基于事件,异步,非阻塞,回调等特性,正是基于这样的一些特性,Node.js平台上的Web框架也具有不同于其他平台的一些特性,其中Connect是众多Web框架中的佼佼者。
Connect在它的官方介绍中,它是Node的一个中间件框架。超过18个捆绑的中间件和一些精选第三方中间件。尽管Connect可能不是性能最好的Node.jsWeb框架,但它却几乎是最为流行的Web框架。为何Connect能在众多框架中胜出,其原因不外乎有如下几个:
模型简单
中间件易于组合和插拔
中间件易于定制和优化
丰富的中间件
Connect自身十分简单,其作用是基于Web服务器做中间件管理
。至于如何处理网络请求,这些任务通过路由分派给管理的中间件们进行处理
。它的处理模型仅仅只是一个中间队列,进行流式处理而已,流式处理可能性能不是最优,但是却是最易于被理解和接受
。基于中间件可以自由组合和插拔的情况, 优化它十分容易。Connect模块目前在NPM仓库的MDO(被依赖最多的模块)排行第八位。但这并没有真实反映出它的价值,因为排行第五位的Express框架实际上是依赖Connect创建而成的
。
Connect是一个node中间件(middleware)框架
。如果把一个http处理过程比作是污水处理,中间件就像是一层层的过滤网。每个中间件在http处理过程中通过改写request或(和)response的数据、状态,实现了特定的功能
。这些功能非常广泛,下图列出了connect所有内置中间件和部分第三方中间件。
下图根据中间件在整个http处理流程的位置,将中间件大致分为3类:
Pre-Request 通常用来改写request的原始数据;
Request/Response 大部分中间件都在这里,功能各异;
Post-Response 全局异常处理,改写response数据等;
2 中间件
让我们回顾一下Node.js最简单的Web服务器是如何编写的:
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的中间件也是扮演这样一个角色,处理请求,然后响应客户端或是让下一个中间件继续处理。如下是一个中间件最朴素的原型:
function (req, res, next) { // 中间件 }
在中间件的上下文中,有着三个变量。分别代表请求对象、响应对象、下一个中间件
。如果当前中间件调用了res.end()结束了响应,执行下一个中间件就显得没有必要。
3 流式处理
为了演示中间件的流式处理,我们可以看看中间件的使用形式:
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 || 'an onymous'); this.stack.push({ route: route, handle: fn }); return this; };
值得注意的是,必须要有一个中间件调用res.end()方法来告知客户端请求已被处理完成,否则客户端将一直处于等待状态
。流式处理也是Node.js中用于流程控制的经典模式
,Connect模块是典型的应用了它。流式处理的好处在于,每一个中间层的职责都是单一的,开发者通过这个模式可以将复杂的业务逻辑进行分解
。
4 Connect安装
通过nodejs安装Connect:
~ D:\workspace\javascript>mkdir nodejs-connect && cd nodejs-connect ~ D:\workspace\javascript\nodejs-connect> npm install connect connect@2.9.0 node_modules\connect ├── methods@0.0.1 ├── uid2@0.0.2 ├── pause@0.0.1 ├── cookie-signature@1.0.1 ├── fresh@0.2.0 ├── qs@0.6.5 ├── bytes@0.2.0 ├── buffer-crc32@0.2.1 ├── cookie@0.1.0 ├── debug@0.7.2 ├── send@0.1.4 (range-parser@0.0.4, mime@1.2.11) └── multiparty@2.1.8 (stream-counter@0.1.0, readable-stream@1.0.17)
尝试做一个最简单的web服务器,增加一个文件:app.js
var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(function (req, res) { res.end('hello world\n'); }) .listen(3000);
启动node:
~ D:\workspace\javascript\nodejs-connect>node app.js GET / 200 5ms GET /favicon.ico 200 0ms
打开浏览器:http://localhost:3000/
5 路由
从前文可以看到其实app.use()方法接受两个参数,route和fn,既路由信息和中间件函数,一个完整的中间件,其实包含路由信息和中间件函数
。路由信息的作用是过滤不匹配的URL
。请求在遇见路由信息不匹配时,直接传递给下一个中间件处理
。通常在调用app.use()注册中间件时,只需要传递一个中间件函数即可。实际上这个过程中,Connect会将/作为该中间件的默认路由,它表示所有的请求都会被该中间件处理
。中间件的优势类似于Java中的过滤器,能够全局性地处理一些事务,使得业务逻辑保持简单。任何事物均有两面性,当你调用app.use()添加中间件的时候,需要考虑的是中间件队列是否太长,因为每一层中间件的调用都是会降低性能的。为了提高性能,在添加中间件的时候,如非全局需求的,尽量附带上精确的路由信息
。以multipart中间件为例,它用于处理表单提交的文件信息,相对而言较为耗费资源。它存在潜在的问题,那就是有可能被人在客户端恶意提交文件,造成服务器资源的浪费。如果不采用路由信息加以限制,那么任何URL都可以被攻击。
app.use("/upload", connect.multipart({ uploadDir: path }));
加上精确的路由信息后,可以将问题减少。
6 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 // 视图层目录
7 静态文件中间件
如何利用Node.js实现一个静态文件服务器的许多技术细节,包括路由实现,MIME,缓存控制,传输压缩,安全、欢迎页、断点续传等
。但是这里我们不需要去亲自处理细节,Connect的static中间件为我们提供上述所有功能。代码只需寥寥3行即可:
var connect = require('connect'); var app = connect(); app.use(connect.static(__dirname + '/public'));
在项目中需要临时搭建静态服务器,也无需安装apache之类的服务器,通过NPM安装Connect之后,三行代码即可解决需求
。这里需要提及的是在使用该模块的一点性能相关的细节。
7.1 动静分离
前一章提及,app.use()方法在没有指定路由信息时,相当于app.use("/", middleware)
。这意味着静态文件中间件将会在处理所有路径的请求。在动静态请求混杂的场景下,静态中间件会在动态请求时也调用fs.stat来检测文件系统是否存在静态文件
。这造成了不必要的系统调用,使得性能降低。解决影响性能的方法既是动静分离。利用路由检测,避免不必要的系统调用, 可以有效降低对动态请求的性能影响。
app.use('/public', connect.static(__dirname + '/public'));
在大型的应用中,动静分离通常无需到一个Node.js实例中进行,CDN的方式直接在域名上将请求分离
。小型应用中,适当的进行动静分离即可避免不必要的性能损耗。
7.2 缓存策略
缓存策略包含客户端和服务端两个部分
。客户端的缓存,主要是利用浏览器对HTTP协议响应头中cache-control和expires字段的支持
。浏览器在得到明确的相应头后,会将文件缓存在本地,依据cache-control和expires的值进行相应的过期策略。这使得重复访问的过程中,浏览器可以从本地缓存中读取文件,而无需从网络读取文件,提升加载速度,也可以降低对服务器的压力。默认情况下静态中间件的最大缓存时设置为0,意味着它在浏览器关闭后就被清除
。这显然不是我们所期望的结果。除非是在开发环境可以无视maxAge的设 置外,生产环境请务必设置缓存,因为它能有效节省网络带宽。
app.use('/public', connect.static(__dirname + '/p ublic', {maxAge: 86400000}));
maxAge选项的单位为毫秒
。YUI3的CDN服务器设置过期时间为10年,是一个值得参考的值。静态文件如果在客户端被缓存,在需要清除缓存的时候,又该如何清除呢?这里的实现方法较多,一种较为推荐的做法是为文件进行md5处理
。
http://some.url/some.js?md5
当文件内容产生改变时,md5值也将发生改变,浏览器根据URL的不同会重新获取静态文件。md5的方式可以避免不必要的缓存清除,也能精确清除缓存
。由于浏览器本身缓存容量的限制,尽管我们可能设置了10年的过期时间,但是也许两天之后就被新的静态文件挤出了本地缓存。这将持续引起静态服务器的响应,也即意味着,客户端缓存并不能完全解决降低服务器压力的问题
。为了解决静态服务器重复读取磁盘造成的压力,这里需要引出第二个相关的中间件:staticCache。
app.use(connect.staticCache()); app.use(“/public”, connect.static(__dirname + '/public', {maxAge: 86400000}));
这是一个提供上层缓存功能的中间件,能够将磁盘中的文件加载到内存中,以提高响应速度和提高性能
。
另一个专门用于静态文件托管的模块叫node-static,其性能是Connect静态文件中间件的效率的两倍
。但是在缓存中间件的协助下,可以弥补性能损失。
事实上,这个中间件在生产环境下并不推荐被使用,而且它将在Connect 3.0版本中被移除。但是它的实现中有值得玩味的地方,这有助于我们认识Node.js模型的优缺点。
staticCache中间件有两个主要的选项:maxObjects和maxLength。代表的是能存储多少个文件和单个文件的最大尺寸,其默认值为128和256kb
。为何会有这两个选项的设定,原因在于V8有内存限制的原因,作为缓存,如果没有良好的过期策略,缓存将会无限增加,直到内存溢出。设置存储数量和单个文件大小后,可以有效抑制缓存区的大小。事实上,该缓存还存在的缺陷是单机情况下
,通常为了有效利用CPU,Node.js实例并不只有一个,多个实例进程之间将会存在冗余的缓存占用,这对于内存使用而言是浪费的。
除此之外,V8的垃圾回收机制是暂停JavaScript线程执行,通过扫描的方式决定是否回收对象。如果缓存对象过大,键太多,则扫描的时间会增加,会引起JavaScript响应业务逻辑的速度变慢。但是这个模块并非没有存在的意义,上述提及的缺陷大多都是V8内存限制和Node.js单线程的原因
。解决该问题的方式则变得明了。
风险转移是Node.js中常用于解决资源不足问题的方式,尤其是内存方面的问题。将缓存点,从Node.js实例进程中转移到第三方成熟的缓存中去即可
。这可以保证:
缓存内容不冗余。
集中式缓存,减少不一致性的发生。
缓存的算法更优秀以保持较高的命中率。
让Node.js保持轻量,以解决它更擅长的问题。
Connect推荐服务器端缓存采用varnish这样的成熟缓存代理
。而笔者目前的项目则是通过Redis来完成后端缓存的任务。
8 Connect内置中间件介绍
logger: 用户请求日志中间件
csrf: 跨域请求伪造保护中间件
compress: gzip压缩中间件
basicAuth: basic认证中间件
bodyParser: 请求内容解析中间件
json: JSON解析中间件
urlencoded: application/x-www-form-urlencode请求解析中间件
multipart: multipart/form-data请求解析中间件
timeout: 请求超时中间件
cookieParser: cookie解析中间件
session: 会话管理中间件
cookieSession: 基于cookies的会话中间件
methodOverride: HTTP伪造中间件
reponseTime: 计算响应时间中间件
staticCache: 缓存中间件
static: 静态文件处理中间件
directory: 目录列表中间件
vhost: 虚拟二级域名映射中间件
favicon: 网页图标中间件
limit: 请求内容大小限制中间件
query: URL解析中间件
errorHadnler: 错误处理中间件
8.1 logger
描述:用来输出用户请求日志。参数:options或者format字符串。
options:
format:参考下面的tokens
stream:输出流,默认是stdout
buffer:缓冲时间,默认是1000ms
immediate:立刻打印日志
tokens: format格式
:req[header] ex: :req[Accept]
:res[header] ex: :res[Content-Length]
:http-version
:response-time
:remote-addr
:date
:method
:url
:referrer
:user-agent
:status
Formats:缩写
default ‘:remote-addr – – [:date] “:method :url HTTP/:http-version” :status :res[content-length] “:referrer” “:user-agent”‘
short ‘:remote-addr – :method :url HTTP/:http-version :status :res[content-length] – :response-time ms’
tiny ‘:method :url :status :res[content-length] – :response-time ms’
dev concise output colored by response status for development use
例子:新建logger.js
var connect = require('connect'); var app = connect() .use(connect.logger()) .use(function (req, res) { res.end('hello world\n'); }) .listen(3000);
connect.logger()
127.0.0.1 - - [Mon, 23 Sep 2013 05:14:18 GMT] "GET / HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKi t/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36" 127.0.0.1 - - [Mon, 23 Sep 2013 05:14:18 GMT] "GET /favicon.ico HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"
connect.logger(‘short’)
127.0.0.1 - GET / HTTP/1.1 200 - - 9 ms 127.0.0.1 - GET /favicon.ico HTTP/1.1 200 - - 1 ms
connect.logger(‘dev’)
GET / 200 5ms GET /favicon.ico 200 1ms
connect.logger(function(tokens, req, res){ return ‘some format string’ })
some format string some format string
所以在开发环境,我们日志设置成dev就好了。
8.2 cookieParser
描述:cookie解析中间件,解析Cookies的头通过req.cookies得到cookies。还可以通过req.secret加密cookies。
例子:新建cookieParser.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.cookieParser('secret string'))
- .use(function (req, res, next) {
- req.cookies.website="blog.fens.me";
- res.end(JSON.stringify(req.cookies));
- })
- .listen(3000);
8.3 session
描述:会话管理中间件
依赖:cookieParser
参数:options
key:Cookies名,默认值为connect.sid
store: session存储实例
secret: session的cookie加密
cookie: session的cookie配置,默认值为{path: ‘/’, httpOnly: true, maxAge: null}
proxy:安全cookie的反向代理,通过x-forwarded-proto实现
Cookie option:
cookie.maxAge: 默认值null,表示当浏览器关闭后cookie被删除。
例子:新建session.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.cookieParser())
- .use(connect.session({secret: '123', cookie: { maxAge: 60000 }}))
- .use(function (req, res, next) {
- if(req.session.pv){
- res.setHeader('Content-Type', 'text/html');
- res.write('views: ' + req.session.pv);
- req.session.pv++;
- res.end();
- }else{
- req.session.pv = 1;
- res.end('Refresh');
- }
- })
- .listen(3000);
8.4 cookieSession
描述:基于cookies的会话中间件
参数:options:
key:Cookies名,默认值为connect.sess
secret: 防止cookie窃取
cookie: session的cookie配置,默认值为{path: ‘/’, httpOnly: true, maxAge: null}
proxy:安全cookie的反向代理,通过x-forwarded-proto实现
Clearing sessions:
req.session = null;
例子:新建cookieSession.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.cookieParser())
- .use(connect.cookieSession({ secret: 'adddaa!', cookie: { maxAge: 60 * 60 * 1000 }}))
- .use(function (req, res, next) {
- if(req.session.pv){
- res.setHeader('Content-Type', 'text/html');
- res.write('views: ' + req.session.pv );
- req.session.pv++;
- console.log(req.session.pv);
- res.end();
- }else{
- req.session.pv = 1;
- res.end('Refresh');
- }
- })
- .listen(3000);
我们发现,这次不管刷新多少次页面,req.session.pv始终是1。
8.5 compress
描述:gzip压缩中间件,通过filter函数设置,需要压缩的文件类型。压缩算法为gzip/deflate。
filter函数:
- exports.filter = function(req, res){
- return /json|text|javascript|dart|image\/svg\+xml|application\/x-font-ttf|application\/vnd\.ms-opentype|application\/vnd\.ms-fontobject/.test(res.getHeader('Content-Type'));
- };
Threshold压缩阈值:当响应请求大于阈值,则压缩响应请求。
参数:options
chunkSize (default: 16*1024)
windowBits
level: 0-9 where 0 is no compression, and 9 is slow but best compression
memLevel: 1-9 low is slower but uses less memory, high is fast but uses more
strategy: compression strategy
例子:新建compress.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.compress({level:9}))
- .use(function (req, res) {
- res.setHeader('Content-Type', 'text/html');
- res.write(res);
- res.end('hello world\n');
- })
- .listen(3000);
8.6 basicAuth
描述:basic认证中间件,实现简单的用户密码登陆,当用户和密码验证通过后,通过一个callback方法继续执行。
同步验证:
- connect()
- .use(connect.basicAuth(function(user, pass){
- return 'tj' == user && 'wahoo' == pass;
- }))
异步验证:
- connect()
- .use(connect.basicAuth(function(user, pass, fn){
- User.authenticate({ user: user, pass: pass }, fn);
- }))
例子:新建basicAuth.js
- var connect = require('connect');
- var app = connect();
- app.use(connect.logger('dev'));
- // 基本用法
- // app.use(connect.basicAuth('fens','fens'))
- // 同步验证
- app.use(connect.basicAuth(function (user, pass) {
- var isLogin = 'fens' == user && 'fens' == pass;
- console.log("Login:" + isLogin);
- return isLogin;
- }))
- app.use(function (req, res) {
- res.end('hello world\n');
- })
- app.listen(3000);
验证弹出框:
正确输入用户和密码后,访问页面:
8.7 bodyParser
描述:请求内容解析中间件,支持多种类型application/json,application/x-www-form-urlencoded, multipart/form-data.
与其他的3个中间件相同:
- app.use(connect.json());
- app.use(connect.urlencoded());
- app.use(connect.multipart());
例子:新建bodyParser.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.bodyParser())
- .use(function(req, res) {
- res.end('req.body=>' + JSON.stringify(req.body));
- })
- .listen(3000);
POST方法:
- ~ curl -d 'user[name]=tj' http://localhost:3000/
- req.body=>{"'user":{"name":"tj'"}}
GET方法:
- ~ curl http://localhost:3000/?user=123
- req.body=>{}
8.8 json
描述:JSON解析中间件,req.body传值
参数:options
strict: when false anything JSON.parse() accepts will be parsed
reviver: used as the second “reviver” argument for JSON.parse
limit: byte limit [1mb]
同bodyParser。
8.9 urlencoded
描述:application/x-www-form-urlencode请求解析中间件
参数:options
limit: byte limit [1mb]
同bodyParser。
8.10 multipart
描述:multipart/form-data请求解析中间件,解析req.body和req.files.
上传文件:uploadDir
app.use(connect.multipart({uploadDir: path }))
参数:options
limit: byte limit defaulting to [100mb]
defer: 在不等待“结束”事件,通过调用req.form.next()函数,可以缓冲并处理多个表单对象,还可以绑定到“progress”或“events”的事件。
Temporary Files:临时文件
默认情况下,临时文件会被保存在os.tmpDir()目录,但不会自动回归,我们必须手动处理。如果没有使用defer选项时,你可以通过req.files来获得对象的使用。
- req.files.images.forEach(function(file){
- console.log(' uploaded : %s %skb : %s', file.originalFilename, file.size / 1024 | 0, file.path);
- });
Streaming:流式处理
当使用defer选项时,文件在上传过程中,你可以通过”part”事件和流控制访问文件。
- req.form.on('part', function(part){
- // transfer to s3 etc
- console.log('upload %s %s', part.name, part.filename);
- var out = fs.createWriteStream('/tmp/' + part.filename);
- part.pipe(out);
- });
- req.form.on('close', function(){
- res.end('uploaded!');
- });
例子:新建multipart.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.multipart({ uploadDir: 'd:\\tmp' }))
- .use(function (req, res) {
- if(req.method=='POST'){
- console.log(req.files);
- res.end('Upload ==>'+ req.files.file.path);
- }
- res.setHeader('Content-Type', 'text/html');
- res.write('<form enctype="multipart/form-data" method="POST"><input type="file" name="file">');
- res.write('<input type="submit" value="submit"/>');
- res.write('</form>');
- res.end('hello world\n');
- }).listen(3000);
通过form表单选择文件:
POST请求解析:
8.11 timeout
描述:请求超时中间件,默认超时时间是5000ms,可以清除这个时间通过req.clearTimeout()函数。超时的错误,可以通过next()函数传递。我们也可以设置超时的响应错误状态码:.timeout=503
例子:新建timeout.js,模拟超时
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.timeout(1000))
- .use(function (req, res) {
- setTimeout(function(){
- res.end('hello world\n');
- },5000)
- })
- .listen(3000);
控制台输出:
- Error: Response timeout
- at IncomingMessage. (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\timeout.j
- s:39:17)
- at IncomingMessage.EventEmitter.emit (events.js:95:17)
- at null._onTimeout (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\timeout.js:34:11)
- at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
- GET / 503 1030ms - 389b
- Error: Response timeout
- at IncomingMessage. (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\timeout.j
- s:39:17)
- at IncomingMessage.EventEmitter.emit (events.js:95:17)
- at null._onTimeout (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\timeout.js:34:11)
- at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
- GET /favicon.ico 503 1006ms - 389b
8.12 reponseTime
描述:计算响应时间中间件,在response的header增加X-Response-Time,计算响应时间。
例子:新建reponseTime.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.responseTime())
- .use(function (req, res) {
- res.end('hello world\n');
- })
- .listen(3000);
8.13 methodOverride
无法模拟出效果,暂时先跳过
描述: 提供伪造HTTP中间件,检查一个method是否被重写,通过传递一个key,得到_method,原始的方法通过req.originalMethod获得。
例子:新建methodOverride.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.methodOverride('KEY'))
- .use(function (req, res) {
- res.end(JSON.stringify(req.headers));
- })
- .listen(3000);
8.14 csrf
描述:跨域请求csrf保护中间件,通过req.csrfToken()令牌函数绑定到请求的表单字段。这个令牌会对访客会话进行验证。默认情况会检查通过bodyParser()产生的req.body,query()函数产生的req.query,和X-CSRF-Token的header。
依赖:session(), cookieParser()
参数:options:
value: 一个函数接受请求,返回的令牌
例子:新建csrf.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.cookieParser())
- .use(connect.session({secret: '123', cookie: { maxAge: 60000 }}))
- .use(connect.csrf({value:'admin'}))
- .use(function (req, res) {
- res.setHeader('Content-Type', 'text/html');
- res.end('hello world\n');
- console.log(req.csrfToken());
- })
- .listen(3000);
生成的csrf Token:
- 1YZ72JuGRTOh/mzqByktPoyz2C+Dk1E5wXyj0=
- GET / 200 356ms
- bItSjAAXydK4jkYYLDnc1c0+7AGDFwGX6r8ns=
- GET /favicon.ico 200 3ms
8.15 static
描述: 静态文件处理中间件,设置root路径作为静态文件服务器
参数:options:
maxAge:浏览器缓存存活时间(毫秒),默认值0
hidden:是否允许传递隐藏类型的文件,默认值false
redirect:是否允许当访问名是一个目录,结尾增加”/”,默认值true
index:设置默认的文件名,默认值index.html
例子:新建static.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.static(__dirname+'/static',{maxAge:60*60*1000,hidden:false}))
- .use(function (req, res) {
- res.setHeader('Content-Type', 'text/html');
- res.write('static:');
- res.write('<img src="static.png" width="100px"/>');
- res.write('hidden:');
- res.end('<img src=".png" width="100px"/>');
- })
- .listen(3000);
隐藏类型的文件,没有被显示出来。
8.16 staticCache
描述: 静态文件缓存中间件,最大可以缓存128个对象,每个对象最大256K,总加起来32mb。缓存算法采用LRU(最近最少使用)算法,让最活跃的对象保存在缓存中,从而增加命中。
依赖:static()
参数:options
maxObjects:最多能缓存的对象,默认值128个
maxLength:最大缓存的对象,默认值256kb
例子:新建staticCache.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.static(__dirname+'/static',{maxAge:60*60*1000,hidden:false}))
- .use(connect.staticCache())
- .use(function (req, res) {
- res.setHeader('Content-Type', 'text/html');
- res.write('static:');
- res.write('<img src="static.png" width="100px"/>');
- res.write('hidden:');
- res.end('<img src=".png" width="100px"/>');
- }).listen(3000);
控制台日志:
- connect.staticCache() is deprecated and will be removed in 3.0
- use varnish or similar reverse proxy caches.
- GET / 200 11ms
- GET /.png 200 0ms
建议用varnish或专门的缓存工具,来代替staticCache()。
8.17 directory
描述: 目录列表中间件,列出指定目录下的文件
参数:options:
hidden:是否显示隐藏文件,默认值false.
icons:是否显示网站图标,默认值false.
filter:是否过滤文件,默认值false.
例子:新建directory.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.directory(__dirname+'/static',{hidden:true}))
- .use(function (req, res) {
- res.end();
- })
- .listen(3000);
8.18 vhost
无法模拟出效果,暂时先跳过
描述: 虚拟二级域名映射中间件,设置hostname和server。
例子:新建vhost.js
- var connect = require('connect');
- var app = connect();
- app.use(connect.logger('dev'));
- app.use(function (req, res) {
- res.end(JSON.stringify(req.headers.host));
- });
- var fooApp = connect();
- fooApp.use(connect.logger('dev'));
- fooApp.use(function (req, res) {
- res.end('hello fooApp\n');
- });
- var barApp = connect();
- barApp.use(connect.logger('dev'));
- barApp.use(function (req, res) {
- res.end('hello barApp\n');
- });
- app.use(connect.vhost('foo.com', fooApp));
- app.use(connect.vhost('bar.com', barApp));
- app.listen(3000);
8.19 favicon
描述:网页图标中间件,指定favicon的路径
参数:options:
maxAge:缓存存活时间(毫秒),默认值1天
例子:新建favicon.js
- var connect = require('connect');
- var app = connect()
- .use(connect.favicon('static/favicon.ico'))
- .use(connect.logger('dev'))
- .use(function (req, res) {
- res.end('hello world\n');
- })
- .listen(3000);
网站图标
8.20 limit
描述: 请求内容大小限制中间件,可以用mb,kb,gb表示单位。
例子:新建limit.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger('dev'))
- .use(connect.limit('1mb'))
- .use(connect.multipart({ uploadDir: 'd:\\tmp' }))
- .use(function (req, res) {
- if(req.method=='POST'){
- console.log(req.files);
- res.end('Upload ==>'+ req.files.file.path);
- }
- res.setHeader('Content-Type', 'text/html');
- res.write('<form enctype="multipart/form-data" method="POST"><input type="file" name="file">');
- res.write('<input type="submit" value="submit"/>');
- res.write('</form>');
- res.end('hello world\n');
- }).listen(3000);
控制台日志,上传大于1mb的文件。
- GET / 200 8ms
- Error: Request Entity Too Large
- at Object.exports.error (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\utils.js:62:13)
- at Object.limit [as handle] (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\limit.js:46:
- 47)
- at next (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:190:15)
- at Object.logger (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\logger.js:156:5)
- at next (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:190:15)
- at Function.app.handle (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:198:3)
- at Server.app (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\connect.js:65:37)
- at Server.EventEmitter.emit (events.js:98:17)
- at HTTPParser.parser.onIncoming (http.js:2027:12)
- at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:119:23)
- POST / 413 8ms - 961b
8.21 query
描述:URL解析中间件,自动解析URL查询参数req.query
参数:options,qs.parse函数
例子:新建query.js
- var connect = require('connect');
- var app = connect()
- .use(connect.query())
- .use(connect.logger('dev'))
- .use(function (req, res) {
- res.end(JSON.stringify(req.query));
- })
- .listen(3000);
通过CURL测试:
- curl -d '{name:'aad'}' http://localhost:3000?pass=did
- {"pass":"did"}
req.query会自动解析URL的查询参数,不解析POST的数据。
8.22 errorHadnler
无法模拟出效果,暂时先跳过
描述:错误处理中间件,对于开发过程中的错误,提供栈跟踪和错误响应,接受3种类型text,html,json。
Text: text/plain是默认类型,返回一个简单的栈跟踪和错误消息
JSON:application/json,返回{‘error’:error}对象
HTML: 返回一个HTML错误页面
参数:options:
showStack:返回错误信息和错误栈.默认值false
showMessage,只返回错误信息,默认值false
dumpExceptions, 输出异常日志,默认值false
logErrors,输出错误日志到文件,默认值false
例子:新建errorHadnler.js
- var connect = require('connect');
- var app = connect()
- .use(connect.logger())
- .use(connect.errorHandler({dumpExceptions: true, showStack: true }))
- .use(function (req, res) {
- req.headers.accept='html';
- res.write(JSON.stringify(req.headers.accept));
- throw new Error('my errorHandler!!!');
- res.end('Hello');
- })
- .listen(3000);
控制台输出:
- rror: my errorHandler!!!
- at Object.handle (D:\workspace\javascript\nodejs-connect\errorHadnler.js:8:15)
- at next (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:190:15)
- at next (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:192:9)
- at Object.logger (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\logger.js:156:5)
- at next (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:190:15)
- at Function.app.handle (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:198:3)
- at Server.app (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\connect.js:65:37)
- at Server.EventEmitter.emit (events.js:98:17)
- at HTTPParser.parser.onIncoming (http.js:2027:12)
- at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:119:23)
深入浅出Node.js---Connect模块解析 。转载的更多相关文章
- Connect模块解析 转载
来自对<了不起的Node.js>一书的学习ConnectNode.js为常规的网络应用提供了基本的API.然而,实际情况下,绝大部分网络应用都需要完成一系列类似的操作,这些类似的操作你一定 ...
- 深入浅出Node.js (2) - 模块机制
2.1 CommonJS规范 2.1.1 CommonJS的出发点 2.1.2 CommonJS的模块规范 2.2 Node的模块实现 2.2.1 优先从缓存加载 2.2.2 路径分析和文件定位 2. ...
- 《深入浅出Node.js》第7章 网络编程
@by Ruth92(转载请注明出处) 第7章 网络编程 Node 只需要几行代码即可构建服务器,无需额外的容器. Node 提供了以下4个模块(适用于服务器端和客户端): net -> TCP ...
- 读书笔记: 深入浅出node.js
>> 深入浅出node.js node.js是c++编写的js运行环境 浏览器: 渲染引擎 + js引擎 后端的js运行环境 node.js用google v8引擎,同时提供很多系统级的A ...
- 深入浅出Node.js(上)
(一):什么是Node.js Node.js从2009年诞生至今,已经发展了两年有余,其成长的速度有目共睹.从在github的访问量超过Rails,到去年底Node.jsS创始人Ryan Dalh加盟 ...
- 《深入浅出Node.js》第6章 理解 Buffer
@by Ruth92(转载请注明出处) 第6章 理解 Buffer ✁ 为什么需要 Buffer? 在 Node 中,应用需要处理网络协议.操作数据库.处理图片.接收上传文件等,在网络流和文件的操作中 ...
- 深入浅出Node.js(一):什么是Node.js
Node.js从2009年诞生至今,已经发展了两年有余,其成长的速度有目共睹.从在github的访问量超过Rails,到去年底Node.jsS创始人Ryan Dalh加盟Joyent获得企业资助,再到 ...
- Node.js工具模块
在Node.js的模块库中提供实用的模块数量. 这些模块都是很常见的,并同时开发基于任何节点的应用程序频繁使用. S.N. 模块的名称和说明 1 OS Module提供基本的操作系统相关的实用功能 2 ...
- 深入浅出Node.js(一):什么是Node.js(转贴)
以下内容转自:http://www.infoq.com/cn/articles/what-is-nodejs/ 作者:崔康 [编者按]:Node.js从2009年诞生至今,已经发展了两年有余,其成长的 ...
- 《深入浅出node.js(朴灵)》【PDF】下载
<深入浅出node.js(朴灵)>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062563 内容简介 <深入浅出Node. ...
随机推荐
- HashMap与TreeMap按照key和value排序
package com.sort; import java.util.ArrayList; import java.util.Collections; import java.util.Compara ...
- set循环遍历删除特定元素
使用Iterator迭代器 public class Demo { public static void main(String[] args) { Set<Object> obj = n ...
- Linux3.10.0块IO子系统流程(1)-- 上层提交请求
Linux通用块层提供给上层的接口函数是submit_bio.上层在构造好bio之后,调用submit_bio提交给通用块层处理. submit_bio函数如下: void submit_bi ...
- Cracking The Coding Interview 5.7
//An array A[1-n] contains all the integers from 0 to n except for one number which is missing. In t ...
- SQL-19 查找所有员工的last_name和first_name以及对应的dept_name,也包括暂时没有分配部门的员工
题目描述 查找所有员工的last_name和first_name以及对应的dept_name,也包括暂时没有分配部门的员工CREATE TABLE `departments` (`dept_no` c ...
- python学习二三事儿(转,整)
Python 标识符 在 Python 里,标识符由字母.数字.下划线组成. 在 Python 中,所有标识符可以包括英文.数字以及下划线(_),但不能以数字开头. Python 中的标识符是区分大小 ...
- android-DNS服务找不到
1.重启eclipse 2.重新建立AVD 3.在建立AVD时sd卡数值不要填
- Vue项目初始化
1. 生成项目模板 vue init <模板名> 本地文件夹名称2. 进入到生成目录里面 cd xxx npm install3. npm run dev vue项目模板介绍: simpl ...
- python Django rest-framework 序列化步骤
django-rest-framework,是一套基于Django 的 REST 框架,是一个强大灵活的构建 Web API 的工具包.本文介绍一下 django-rest-framework 的简单 ...
- MSDN Windows各版本哈希值
Windows 7 Enterprise (x64) - DVD (English)文件名 en_windows_7_enterprise_x64_dvd_x15-70749.isoSHA1: A89 ...