Connect是一个中间件框架,负责管理、调度中间件,中间件的思想最早源于Ruby的Rack框架。
Connect的代码非常少,只有200多行,因此我强烈建议你去阅读Connect的源码。你会看到Connect做的事情非常少,它的目的就是为了让其它框架基于自己实现更丰富的功能,事实证明这个策略是成功的。
虽然Connect早期也自带了一些中间件实现,但从核心功能上来说Connect不需要自带中间件。从3.0之后Connect自带的中间件就被移到单独的项目了,所以现在的Connect本身没有提供任何中间件,这也是最新的Connect代码量很少的原因。
Connect框架基本用法
中间件是一个函数,函数原型是function (req, res, next) {}
。为了更好地理解,我们下面实现几个简单的中间件,并将这几个中间件串联起来构成一个Web应用。该应用的功能是在网页上输出"Hello World",如果用户通过URL Get参数指明了自己的名字那么就输出"Hello 名字"。
var connect = require('connect')
var http = require('http')
// 这个中间件的功能是:将用户的HTTP请求打印在控制台上
function logger(req, resp, next) {
console.log('%s, %s', req.method, req.url)
console.log(req.params)
next() // 将用户请求继续传递给下一个中间件处理
}
// 这个中间件的功能是:不管用户访问什么,都返回一个"Hello World"信息给用户
function hello_world(req, resp, next) {
resp.setHeader('Content-Type', 'text/plain')
resp.end('Hello World')
// 这里不调用next(),说明用户请求被这个中间件“拦截”了,不再继续传递给下一个中间件处理
}
// 这个中间件的功能是:将用户URL的参数解析并存放在req.url_params
// 例如用户浏览器输入 http://localhost:3000?name=chend&age=24
// 那么req.url_params就会变成: {'name': 'chend', 'age': '24'}
function parse_url_parameter(req, resp, next) {
req.url_params = {}
if (req.url.indexOf('?') != -1) {
var params_string = req.url.substr(req.url.indexOf('?') + 1)
var params_arr = params_string.split('&')
params_arr.forEach(function(param) {
var key = param.substr(0, param.indexOf('='))
var val = param.substr(param.indexOf('=') + 1)
req.url_params[key] = val
})
}
next()
}
// 这个中间件的功能是:从用户输入的URL Get参数中读取name变量,然后返回"Hello <name>"给用户,如果用户的URL没有name变量就不做任何事情
// 例如用户在浏览器输入`http://localhost:3000/?name=chend`,那么我们会返回"Hello chend"给用户
function hello(req, resp, next) {
if (req.url_params && req.url_params.name) {
resp.setHeader('Content-Type', 'text/plain')
resp.end('Hello ' + req.url_params.name)
} else {
// 本中间件无法处理,就调用next()将用户请求传递给下一个中间件处理吧
next()
}
}
var app = connect()
// 将上面定义的4个中间件按照这样的顺序串联起来,再次强调是按照顺序的
app.use(logger).use(parse_url_parameter).use(hello).use(hello_world)
http.createServer(app).listen(3000)
Connect实现原理
因为Connect核心逻辑很简单,我们这里就直接实现一个精简版的Connect帮助大家理解它是怎么工作的。
我们知道Node.js自带的http
模块是这样用的(业务要实现handle函数):
var http = require('http')
var handle = function(req, resp) {
resp.writeHead(200, {'Content-Type': "text/plain"})
resp.end("Welcome!")
}
var server = http.createServer(handle)
server.listen(3000)
下面我们定义一个app对象,能够像handle那样被当作函数一样调用,又能实现更加复杂的功能:
function createServer() {
var app = function (req, resp) {
app.handle(req, resp)
}
app.queue = []
app.handle = function handle(req, resp) {
// 当用户在浏览器敲回车,http模块会调用一次该函数
var index = 0
var queue = this.queue
function next() {
var layer = queue[index++]
if (!layer) { return } // 所有路由规则遍历完毕
if (layer.route != req.url) { // 路由匹配功能
return next()
}
layer.handle(req, resp, next) // next传递给业务中间件,由中间件决定是否调用next来传递给下一个中间件
}
next() // 调用一次next,就一次
}
app.use = function use(path, fn) {
this.queue.push({ route: path, handle: fn })
}
return app
}
module.exports = createServer
Connect官方实现的中间件
光有Connect这个中间件框架还不够,真正实现业务逻辑的是中间件本身。本章介绍由Connect/Express团队维护的一些官方中间件,利用这些中间件可以组成一个强大的Web应用。
-
body-parser为后续中间件提供
req.body
和req.files
变量。 - compression可以用gzip压缩HTTP响应,让用户感觉网页加载很快。
-
connect-timeout可以设置一个倒计时;后续中间件可以通过
req.timedout
这个布尔变量判断是否超时,也可以通过req.clearTimeout
来取消倒计时;其原理是用过Node.js自带的setTimeout()
和clearTimeout()
实现。 -
cookie-parser为后续中间件提供
req.cookies
和req.signedCookies
-
cookie-session为后续中间件提供
req.session
- 等等
网友评论