Node.js生态:Connect框架

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.bodyreq.files变量。
  • compression可以用gzip压缩HTTP响应,让用户感觉网页加载很快。
  • connect-timeout可以设置一个倒计时;后续中间件可以通过req.timedout这个布尔变量判断是否超时,也可以通过req.clearTimeout来取消倒计时;其原理是用过Node.js自带的setTimeout()clearTimeout()实现。
  • cookie-parser为后续中间件提供req.cookiesreq.signedCookies
  • cookie-session为后续中间件提供req.session
  • 等等
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容